فهرست منبع

Nonblocking RPC for cli (#8687)

* Make CLI async

* nonblocking rpc for CLI

* rm last AsyncRpcClient aliases

* multi-thread test-validator

* rm bad map

* cut unnecessary worker threads

* clean some comments

* DRY for test validator setup

* allow(deprecated) on use, not on file

* use Option<TpuClient> in process_ping

* tpu ping test etc

* rm random whitespace

* fmt

* restore original param order

* try more efficient wait for program deploy

* breakout client when possible; shrink test-validator diff

* cleanup

* correct behavior for wait_for_upgradeable_programs_deployed; send in parallel

* back to current_thread, rm worker_threads=1

* move uses to tof

* rm stray worker_threads

* clean up test-validator file

* fix conflicts typo

* resolve enable_scheduler_bindings conflict

* rm stray worker_threads param

* use solana_tpu_client import when possible

* add worker_threads = 1

* return RpcClientError, with some flakiness tolerance

---------

Co-authored-by: Steven Luscher <steven.luscher@anza.xyz>
Peter Keay 1 روز پیش
والد
کامیت
27b7339dfc

+ 2 - 0
Cargo.lock

@@ -7816,6 +7816,7 @@ dependencies = [
  "test-case",
  "thiserror 2.0.17",
  "tiny-bip39",
+ "tokio",
 ]
 
 [[package]]
@@ -11505,6 +11506,7 @@ dependencies = [
  "solana-transaction",
  "solana-transaction-status",
  "solana-version",
+ "tokio",
 ]
 
 [[package]]

+ 7 - 3
cargo-registry/src/crate_handler.rs

@@ -1,3 +1,5 @@
+#[allow(deprecated)]
+use solana_cli::program_v4::{process_deploy_program_sync, process_dump_sync};
 use {
     crate::{
         client::Client,
@@ -12,7 +14,7 @@ use {
     serde::{Deserialize, Serialize},
     serde_json::from_slice,
     sha2::{Digest, Sha256},
-    solana_cli::program_v4::{process_deploy_program, process_dump, AdditionalCliConfig},
+    solana_cli::program_v4::AdditionalCliConfig,
     solana_keypair::Keypair,
     solana_pubkey::Pubkey,
     solana_signer::{EncodableKey, Signer},
@@ -103,6 +105,7 @@ pub(crate) struct Program {
 }
 
 impl Program {
+    #[allow(deprecated)] // Using sync wrapper for now, should migrate to async
     fn deploy(&self, client: Arc<Client>, signer: &dyn Signer) -> Result<(), Error> {
         if self.id != signer.pubkey() {
             return Err("Signer doesn't match program ID".into());
@@ -125,7 +128,7 @@ impl Program {
             program_data.extend_from_slice(&crate_len);
         }
 
-        process_deploy_program(
+        process_deploy_program_sync(
             client.rpc_client.clone(),
             &cli_config,
             &AdditionalCliConfig::default(),
@@ -144,10 +147,11 @@ impl Program {
         Ok(())
     }
 
+    #[allow(deprecated)] // Using sync wrapper for now, should migrate to async
     fn dump(&mut self, client: Arc<Client>) -> Result<(), Error> {
         info!("Fetching program {:?}", self.id);
 
-        process_dump(
+        process_dump_sync(
             client.rpc_client.clone(),
             &client.get_cli_config(),
             Some(self.id),

+ 1 - 0
cli/Cargo.toml

@@ -102,6 +102,7 @@ solana-vote-program = { workspace = true }
 spl-memo-interface = { version = "=2.0.0" }
 thiserror = { workspace = true }
 tiny-bip39 = { workspace = true }
+tokio = { workspace = true }
 
 [dev-dependencies]
 assert_matches = { workspace = true }

+ 135 - 106
cli/src/address_lookup_table.rs

@@ -17,7 +17,7 @@ use {
     solana_message::Message,
     solana_pubkey::Pubkey,
     solana_remote_wallet::remote_wallet::RemoteWalletManager,
-    solana_rpc_client::rpc_client::RpcClient,
+    solana_rpc_client::nonblocking::rpc_client::RpcClient,
     solana_rpc_client_api::config::RpcSendTransactionConfig,
     solana_sdk_ids::sysvar,
     solana_signer::Signer,
@@ -469,9 +469,9 @@ pub fn parse_address_lookup_table_subcommand(
     Ok(response)
 }
 
-pub fn process_address_lookup_table_subcommand(
+pub async fn process_address_lookup_table_subcommand(
     rpc_client: Arc<RpcClient>,
-    config: &CliConfig,
+    config: &CliConfig<'_>,
     subcommand: &AddressLookupTableCliCommand,
 ) -> ProcessResult {
     match subcommand {
@@ -480,69 +480,83 @@ pub fn process_address_lookup_table_subcommand(
             payer_signer_index,
         } => {
             process_create_lookup_table(&rpc_client, config, *authority_pubkey, *payer_signer_index)
+                .await
         }
         AddressLookupTableCliCommand::FreezeLookupTable {
             lookup_table_pubkey,
             authority_signer_index,
             bypass_warning,
-        } => process_freeze_lookup_table(
-            &rpc_client,
-            config,
-            *lookup_table_pubkey,
-            *authority_signer_index,
-            *bypass_warning,
-        ),
+        } => {
+            process_freeze_lookup_table(
+                &rpc_client,
+                config,
+                *lookup_table_pubkey,
+                *authority_signer_index,
+                *bypass_warning,
+            )
+            .await
+        }
         AddressLookupTableCliCommand::ExtendLookupTable {
             lookup_table_pubkey,
             authority_signer_index,
             payer_signer_index,
             new_addresses,
-        } => process_extend_lookup_table(
-            &rpc_client,
-            config,
-            *lookup_table_pubkey,
-            *authority_signer_index,
-            *payer_signer_index,
-            new_addresses.to_vec(),
-        ),
+        } => {
+            process_extend_lookup_table(
+                &rpc_client,
+                config,
+                *lookup_table_pubkey,
+                *authority_signer_index,
+                *payer_signer_index,
+                new_addresses.to_vec(),
+            )
+            .await
+        }
         AddressLookupTableCliCommand::DeactivateLookupTable {
             lookup_table_pubkey,
             authority_signer_index,
             bypass_warning,
-        } => process_deactivate_lookup_table(
-            &rpc_client,
-            config,
-            *lookup_table_pubkey,
-            *authority_signer_index,
-            *bypass_warning,
-        ),
+        } => {
+            process_deactivate_lookup_table(
+                &rpc_client,
+                config,
+                *lookup_table_pubkey,
+                *authority_signer_index,
+                *bypass_warning,
+            )
+            .await
+        }
         AddressLookupTableCliCommand::CloseLookupTable {
             lookup_table_pubkey,
             authority_signer_index,
             recipient_pubkey,
-        } => process_close_lookup_table(
-            &rpc_client,
-            config,
-            *lookup_table_pubkey,
-            *authority_signer_index,
-            *recipient_pubkey,
-        ),
+        } => {
+            process_close_lookup_table(
+                &rpc_client,
+                config,
+                *lookup_table_pubkey,
+                *authority_signer_index,
+                *recipient_pubkey,
+            )
+            .await
+        }
         AddressLookupTableCliCommand::ShowLookupTable {
             lookup_table_pubkey,
-        } => process_show_lookup_table(&rpc_client, config, *lookup_table_pubkey),
+        } => process_show_lookup_table(&rpc_client, config, *lookup_table_pubkey).await,
     }
 }
 
-fn process_create_lookup_table(
+async fn process_create_lookup_table(
     rpc_client: &RpcClient,
-    config: &CliConfig,
+    config: &CliConfig<'_>,
     authority_address: Pubkey,
     payer_signer_index: usize,
 ) -> ProcessResult {
     let payer_signer = config.signers[payer_signer_index];
 
     let get_clock_result = rpc_client
-        .get_account_with_commitment(&sysvar::clock::id(), CommitmentConfig::finalized())?;
+        .get_account_with_commitment(&sysvar::clock::id(), CommitmentConfig::finalized())
+        .await?;
     let clock_account = get_clock_result.value.expect("Clock account doesn't exist");
     let clock: Clock = from_account(&clock_account).ok_or_else(|| {
         CliError::RpcRequestError("Failed to deserialize clock sysvar".to_string())
@@ -552,7 +566,7 @@ fn process_create_lookup_table(
     let (create_lookup_table_ix, lookup_table_address) =
         create_lookup_table(authority_address, payer_address, clock.slot);
 
-    let blockhash = rpc_client.get_latest_blockhash()?;
+    let blockhash = rpc_client.get_latest_blockhash().await?;
     let mut tx = Transaction::new_unsigned(Message::new(
         &[create_lookup_table_ix],
         Some(&config.signers[0].pubkey()),
@@ -560,15 +574,17 @@ fn process_create_lookup_table(
 
     let keypairs: Vec<&dyn Signer> = vec![config.signers[0], payer_signer];
     tx.try_sign(&keypairs, blockhash)?;
-    let result = rpc_client.send_and_confirm_transaction_with_spinner_and_config(
-        &tx,
-        config.commitment,
-        RpcSendTransactionConfig {
-            skip_preflight: false,
-            preflight_commitment: Some(config.commitment.commitment),
-            ..RpcSendTransactionConfig::default()
-        },
-    );
+    let result = rpc_client
+        .send_and_confirm_transaction_with_spinner_and_config(
+            &tx,
+            config.commitment,
+            RpcSendTransactionConfig {
+                skip_preflight: false,
+                preflight_commitment: Some(config.commitment.commitment),
+                ..RpcSendTransactionConfig::default()
+            },
+        )
+        .await;
     match result {
         Err(err) => Err(format!("Create failed: {err}").into()),
         Ok(signature) => Ok(config
@@ -584,17 +600,18 @@ pub const FREEZE_LOOKUP_TABLE_WARNING: &str =
     "WARNING! Once a lookup table is frozen, it can never be modified or unfrozen again. To \
      proceed with freezing, rerun the `freeze` command with the `--bypass-warning` flag";
 
-fn process_freeze_lookup_table(
+async fn process_freeze_lookup_table(
     rpc_client: &RpcClient,
-    config: &CliConfig,
+    config: &CliConfig<'_>,
     lookup_table_pubkey: Pubkey,
     authority_signer_index: usize,
     bypass_warning: bool,
 ) -> ProcessResult {
     let authority_signer = config.signers[authority_signer_index];
 
-    let get_lookup_table_result =
-        rpc_client.get_account_with_commitment(&lookup_table_pubkey, config.commitment)?;
+    let get_lookup_table_result = rpc_client
+        .get_account_with_commitment(&lookup_table_pubkey, config.commitment)
+        .await?;
     let lookup_table_account = get_lookup_table_result.value.ok_or_else(|| {
         format!("Lookup table account {lookup_table_pubkey} not found, was it already closed?")
     })?;
@@ -613,22 +630,24 @@ fn process_freeze_lookup_table(
     let authority_address = authority_signer.pubkey();
     let freeze_lookup_table_ix = freeze_lookup_table(lookup_table_pubkey, authority_address);
 
-    let blockhash = rpc_client.get_latest_blockhash()?;
+    let blockhash = rpc_client.get_latest_blockhash().await?;
     let mut tx = Transaction::new_unsigned(Message::new(
         &[freeze_lookup_table_ix],
         Some(&config.signers[0].pubkey()),
     ));
 
     tx.try_sign(&[config.signers[0], authority_signer], blockhash)?;
-    let result = rpc_client.send_and_confirm_transaction_with_spinner_and_config(
-        &tx,
-        config.commitment,
-        RpcSendTransactionConfig {
-            skip_preflight: false,
-            preflight_commitment: Some(config.commitment.commitment),
-            ..RpcSendTransactionConfig::default()
-        },
-    );
+    let result = rpc_client
+        .send_and_confirm_transaction_with_spinner_and_config(
+            &tx,
+            config.commitment,
+            RpcSendTransactionConfig {
+                skip_preflight: false,
+                preflight_commitment: Some(config.commitment.commitment),
+                ..RpcSendTransactionConfig::default()
+            },
+        )
+        .await;
     match result {
         Err(err) => Err(format!("Freeze failed: {err}").into()),
         Ok(signature) => Ok(config.output_format.formatted_string(&CliSignature {
@@ -637,9 +656,9 @@ fn process_freeze_lookup_table(
     }
 }
 
-fn process_extend_lookup_table(
+async fn process_extend_lookup_table(
     rpc_client: &RpcClient,
-    config: &CliConfig,
+    config: &CliConfig<'_>,
     lookup_table_pubkey: Pubkey,
     authority_signer_index: usize,
     payer_signer_index: usize,
@@ -652,8 +671,9 @@ fn process_extend_lookup_table(
         return Err("Lookup tables must be extended by at least one address".into());
     }
 
-    let get_lookup_table_result =
-        rpc_client.get_account_with_commitment(&lookup_table_pubkey, config.commitment)?;
+    let get_lookup_table_result = rpc_client
+        .get_account_with_commitment(&lookup_table_pubkey, config.commitment)
+        .await?;
     let lookup_table_account = get_lookup_table_result.value.ok_or_else(|| {
         format!("Lookup table account {lookup_table_pubkey} not found, was it already closed?")
     })?;
@@ -674,22 +694,24 @@ fn process_extend_lookup_table(
         new_addresses,
     );
 
-    let blockhash = rpc_client.get_latest_blockhash()?;
+    let blockhash = rpc_client.get_latest_blockhash().await?;
     let mut tx = Transaction::new_unsigned(Message::new(
         &[extend_lookup_table_ix],
         Some(&config.signers[0].pubkey()),
     ));
 
     tx.try_sign(&[config.signers[0], authority_signer], blockhash)?;
-    let result = rpc_client.send_and_confirm_transaction_with_spinner_and_config(
-        &tx,
-        config.commitment,
-        RpcSendTransactionConfig {
-            skip_preflight: false,
-            preflight_commitment: Some(config.commitment.commitment),
-            ..RpcSendTransactionConfig::default()
-        },
-    );
+    let result = rpc_client
+        .send_and_confirm_transaction_with_spinner_and_config(
+            &tx,
+            config.commitment,
+            RpcSendTransactionConfig {
+                skip_preflight: false,
+                preflight_commitment: Some(config.commitment.commitment),
+                ..RpcSendTransactionConfig::default()
+            },
+        )
+        .await;
     match result {
         Err(err) => Err(format!("Extend failed: {err}").into()),
         Ok(signature) => Ok(config.output_format.formatted_string(&CliSignature {
@@ -703,17 +725,18 @@ pub const DEACTIVATE_LOOKUP_TABLE_WARNING: &str =
 Deactivated lookup tables may only be closed and cannot be recreated at the same address. To \
      proceed with deactivation, rerun the `deactivate` command with the `--bypass-warning` flag";
 
-fn process_deactivate_lookup_table(
+async fn process_deactivate_lookup_table(
     rpc_client: &RpcClient,
-    config: &CliConfig,
+    config: &CliConfig<'_>,
     lookup_table_pubkey: Pubkey,
     authority_signer_index: usize,
     bypass_warning: bool,
 ) -> ProcessResult {
     let authority_signer = config.signers[authority_signer_index];
 
-    let get_lookup_table_result =
-        rpc_client.get_account_with_commitment(&lookup_table_pubkey, config.commitment)?;
+    let get_lookup_table_result = rpc_client
+        .get_account_with_commitment(&lookup_table_pubkey, config.commitment)
+        .await?;
     let lookup_table_account = get_lookup_table_result.value.ok_or_else(|| {
         format!("Lookup table account {lookup_table_pubkey} not found, was it already closed?")
     })?;
@@ -733,22 +756,24 @@ fn process_deactivate_lookup_table(
     let deactivate_lookup_table_ix =
         deactivate_lookup_table(lookup_table_pubkey, authority_address);
 
-    let blockhash = rpc_client.get_latest_blockhash()?;
+    let blockhash = rpc_client.get_latest_blockhash().await?;
     let mut tx = Transaction::new_unsigned(Message::new(
         &[deactivate_lookup_table_ix],
         Some(&config.signers[0].pubkey()),
     ));
 
     tx.try_sign(&[config.signers[0], authority_signer], blockhash)?;
-    let result = rpc_client.send_and_confirm_transaction_with_spinner_and_config(
-        &tx,
-        config.commitment,
-        RpcSendTransactionConfig {
-            skip_preflight: false,
-            preflight_commitment: Some(config.commitment.commitment),
-            ..RpcSendTransactionConfig::default()
-        },
-    );
+    let result = rpc_client
+        .send_and_confirm_transaction_with_spinner_and_config(
+            &tx,
+            config.commitment,
+            RpcSendTransactionConfig {
+                skip_preflight: false,
+                preflight_commitment: Some(config.commitment.commitment),
+                ..RpcSendTransactionConfig::default()
+            },
+        )
+        .await;
     match result {
         Err(err) => Err(format!("Deactivate failed: {err}").into()),
         Ok(signature) => Ok(config.output_format.formatted_string(&CliSignature {
@@ -757,17 +782,18 @@ fn process_deactivate_lookup_table(
     }
 }
 
-fn process_close_lookup_table(
+async fn process_close_lookup_table(
     rpc_client: &RpcClient,
-    config: &CliConfig,
+    config: &CliConfig<'_>,
     lookup_table_pubkey: Pubkey,
     authority_signer_index: usize,
     recipient_pubkey: Pubkey,
 ) -> ProcessResult {
     let authority_signer = config.signers[authority_signer_index];
 
-    let get_lookup_table_result =
-        rpc_client.get_account_with_commitment(&lookup_table_pubkey, config.commitment)?;
+    let get_lookup_table_result = rpc_client
+        .get_account_with_commitment(&lookup_table_pubkey, config.commitment)
+        .await?;
     let lookup_table_account = get_lookup_table_result.value.ok_or_else(|| {
         format!("Lookup table account {lookup_table_pubkey} not found, was it already closed?")
     })?;
@@ -792,22 +818,24 @@ fn process_close_lookup_table(
     let close_lookup_table_ix =
         close_lookup_table(lookup_table_pubkey, authority_address, recipient_pubkey);
 
-    let blockhash = rpc_client.get_latest_blockhash()?;
+    let blockhash = rpc_client.get_latest_blockhash().await?;
     let mut tx = Transaction::new_unsigned(Message::new(
         &[close_lookup_table_ix],
         Some(&config.signers[0].pubkey()),
     ));
 
     tx.try_sign(&[config.signers[0], authority_signer], blockhash)?;
-    let result = rpc_client.send_and_confirm_transaction_with_spinner_and_config(
-        &tx,
-        config.commitment,
-        RpcSendTransactionConfig {
-            skip_preflight: false,
-            preflight_commitment: Some(config.commitment.commitment),
-            ..RpcSendTransactionConfig::default()
-        },
-    );
+    let result = rpc_client
+        .send_and_confirm_transaction_with_spinner_and_config(
+            &tx,
+            config.commitment,
+            RpcSendTransactionConfig {
+                skip_preflight: false,
+                preflight_commitment: Some(config.commitment.commitment),
+                ..RpcSendTransactionConfig::default()
+            },
+        )
+        .await;
     match result {
         Err(err) => Err(format!("Close failed: {err}").into()),
         Ok(signature) => Ok(config.output_format.formatted_string(&CliSignature {
@@ -816,13 +844,14 @@ fn process_close_lookup_table(
     }
 }
 
-fn process_show_lookup_table(
+async fn process_show_lookup_table(
     rpc_client: &RpcClient,
-    config: &CliConfig,
+    config: &CliConfig<'_>,
     lookup_table_pubkey: Pubkey,
 ) -> ProcessResult {
-    let get_lookup_table_result =
-        rpc_client.get_account_with_commitment(&lookup_table_pubkey, config.commitment)?;
+    let get_lookup_table_result = rpc_client
+        .get_account_with_commitment(&lookup_table_pubkey, config.commitment)
+        .await?;
     let lookup_table_account = get_lookup_table_result.value.ok_or_else(|| {
         format!("Lookup table account {lookup_table_pubkey} not found, was it already closed?")
     })?;

+ 69 - 35
cli/src/checks.rs

@@ -1,19 +1,19 @@
 use {
     crate::cli::CliError, solana_cli_output::display::build_balance_message,
     solana_commitment_config::CommitmentConfig, solana_message::Message, solana_pubkey::Pubkey,
-    solana_rpc_client::rpc_client::RpcClient,
+    solana_rpc_client::nonblocking::rpc_client::RpcClient,
     solana_rpc_client_api::client_error::Result as ClientResult,
 };
 
-pub fn check_account_for_fee(
+pub async fn check_account_for_fee(
     rpc_client: &RpcClient,
     account_pubkey: &Pubkey,
     message: &Message,
 ) -> Result<(), CliError> {
-    check_account_for_multiple_fees(rpc_client, account_pubkey, &[message])
+    check_account_for_multiple_fees(rpc_client, account_pubkey, &[message]).await
 }
 
-pub fn check_account_for_fee_with_commitment(
+pub async fn check_account_for_fee_with_commitment(
     rpc_client: &RpcClient,
     account_pubkey: &Pubkey,
     message: &Message,
@@ -25,9 +25,10 @@ pub fn check_account_for_fee_with_commitment(
         &[message],
         commitment,
     )
+    .await
 }
 
-pub fn check_account_for_multiple_fees(
+pub async fn check_account_for_multiple_fees(
     rpc_client: &RpcClient,
     account_pubkey: &Pubkey,
     messages: &[&Message],
@@ -38,9 +39,10 @@ pub fn check_account_for_multiple_fees(
         messages,
         CommitmentConfig::default(),
     )
+    .await
 }
 
-pub fn check_account_for_multiple_fees_with_commitment(
+pub async fn check_account_for_multiple_fees_with_commitment(
     rpc_client: &RpcClient,
     account_pubkey: &Pubkey,
     messages: &[&Message],
@@ -53,16 +55,17 @@ pub fn check_account_for_multiple_fees_with_commitment(
         messages,
         commitment,
     )
+    .await
 }
 
-pub fn check_account_for_spend_multiple_fees_with_commitment(
+pub async fn check_account_for_spend_multiple_fees_with_commitment(
     rpc_client: &RpcClient,
     account_pubkey: &Pubkey,
     balance: u64,
     messages: &[&Message],
     commitment: CommitmentConfig,
 ) -> Result<(), CliError> {
-    let fee = get_fee_for_messages(rpc_client, messages)?;
+    let fee = get_fee_for_messages(rpc_client, messages).await?;
     check_account_for_spend_and_fee_with_commitment(
         rpc_client,
         account_pubkey,
@@ -70,9 +73,10 @@ pub fn check_account_for_spend_multiple_fees_with_commitment(
         fee,
         commitment,
     )
+    .await
 }
 
-pub fn check_account_for_spend_and_fee_with_commitment(
+pub async fn check_account_for_spend_and_fee_with_commitment(
     rpc_client: &RpcClient,
     account_pubkey: &Pubkey,
     balance: u64,
@@ -93,7 +97,9 @@ pub fn check_account_for_spend_and_fee_with_commitment(
         account_pubkey,
         required_balance,
         commitment,
-    )? {
+    )
+    .await?
+    {
         if balance > 0 {
             return Err(CliError::InsufficientFundsForSpendAndFee(
                 build_balance_message(balance, false, false),
@@ -110,19 +116,21 @@ pub fn check_account_for_spend_and_fee_with_commitment(
     Ok(())
 }
 
-pub fn get_fee_for_messages(
+pub async fn get_fee_for_messages(
     rpc_client: &RpcClient,
     messages: &[&Message],
 ) -> Result<u64, CliError> {
-    Ok(messages
-        .iter()
-        .map(|message| rpc_client.get_fee_for_message(*message))
-        .collect::<Result<Vec<_>, _>>()?
-        .iter()
-        .sum())
+    let mut total_fee = 0u64;
+    for message in messages {
+        let fee = rpc_client.get_fee_for_message(*message).await?;
+        total_fee = total_fee
+            .checked_add(fee)
+            .ok_or(CliError::BadParameter("Fee overflow".to_string()))?;
+    }
+    Ok(total_fee)
 }
 
-pub fn check_account_for_balance(
+pub async fn check_account_for_balance(
     rpc_client: &RpcClient,
     account_pubkey: &Pubkey,
     balance: u64,
@@ -133,16 +141,18 @@ pub fn check_account_for_balance(
         balance,
         CommitmentConfig::default(),
     )
+    .await
 }
 
-pub fn check_account_for_balance_with_commitment(
+pub async fn check_account_for_balance_with_commitment(
     rpc_client: &RpcClient,
     account_pubkey: &Pubkey,
     balance: u64,
     commitment: CommitmentConfig,
 ) -> ClientResult<bool> {
     let lamports = rpc_client
-        .get_balance_with_commitment(account_pubkey, commitment)?
+        .get_balance_with_commitment(account_pubkey, commitment)
+        .await?
         .value;
     if lamports != 0 && lamports >= balance {
         return Ok(true);
@@ -177,8 +187,8 @@ mod tests {
         std::collections::HashMap,
     };
 
-    #[test]
-    fn test_check_account_for_fees() {
+    #[tokio::test]
+    async fn test_check_account_for_fees() {
         let account_balance = 1;
         let account_balance_response = json!(Response {
             context: RpcResponseContext {
@@ -201,7 +211,9 @@ mod tests {
         let mut mocks = HashMap::new();
         mocks.insert(RpcRequest::GetBalance, account_balance_response.clone());
         let rpc_client = RpcClient::new_mock_with_mocks("".to_string(), mocks);
-        check_account_for_fee(&rpc_client, &pubkey, &message0).expect("unexpected result");
+        check_account_for_fee(&rpc_client, &pubkey, &message0)
+            .await
+            .expect("unexpected result");
 
         let check_fee_response = json!(Response {
             context: RpcResponseContext {
@@ -214,7 +226,9 @@ mod tests {
         mocks.insert(RpcRequest::GetFeeForMessage, check_fee_response);
         mocks.insert(RpcRequest::GetBalance, account_balance_response.clone());
         let rpc_client = RpcClient::new_mock_with_mocks("".to_string(), mocks);
-        assert!(check_account_for_fee(&rpc_client, &pubkey, &message1).is_err());
+        assert!(check_account_for_fee(&rpc_client, &pubkey, &message1)
+            .await
+            .is_err());
 
         let check_fee_response = json!(Response {
             context: RpcResponseContext {
@@ -228,7 +242,9 @@ mod tests {
         mocks.insert(RpcRequest::GetBalance, account_balance_response);
         let rpc_client = RpcClient::new_mock_with_mocks("".to_string(), mocks);
         assert!(
-            check_account_for_multiple_fees(&rpc_client, &pubkey, &[&message0, &message0]).is_err()
+            check_account_for_multiple_fees(&rpc_client, &pubkey, &[&message0, &message0])
+                .await
+                .is_err()
         );
 
         let account_balance = 2;
@@ -253,11 +269,12 @@ mod tests {
         let rpc_client = RpcClient::new_mock_with_mocks("".to_string(), mocks);
 
         check_account_for_multiple_fees(&rpc_client, &pubkey, &[&message0, &message0])
+            .await
             .expect("unexpected result");
     }
 
-    #[test]
-    fn test_check_account_for_balance() {
+    #[tokio::test]
+    async fn test_check_account_for_balance() {
         let account_balance = 50;
         let account_balance_response = json!(Response {
             context: RpcResponseContext {
@@ -272,13 +289,23 @@ mod tests {
         mocks.insert(RpcRequest::GetBalance, account_balance_response);
         let rpc_client = RpcClient::new_mock_with_mocks("".to_string(), mocks);
 
-        assert!(check_account_for_balance(&rpc_client, &pubkey, 1).unwrap());
-        assert!(check_account_for_balance(&rpc_client, &pubkey, account_balance).unwrap());
-        assert!(!check_account_for_balance(&rpc_client, &pubkey, account_balance + 1).unwrap());
+        assert!(check_account_for_balance(&rpc_client, &pubkey, 1)
+            .await
+            .unwrap());
+        assert!(
+            check_account_for_balance(&rpc_client, &pubkey, account_balance)
+                .await
+                .unwrap()
+        );
+        assert!(
+            !check_account_for_balance(&rpc_client, &pubkey, account_balance + 1)
+                .await
+                .unwrap()
+        );
     }
 
-    #[test]
-    fn test_get_fee_for_messages() {
+    #[tokio::test]
+    async fn test_get_fee_for_messages() {
         let check_fee_response = json!(Response {
             context: RpcResponseContext {
                 slot: 1,
@@ -291,14 +318,19 @@ mod tests {
         let rpc_client = RpcClient::new_mock_with_mocks("".to_string(), mocks);
 
         // No messages, no fee.
-        assert_eq!(get_fee_for_messages(&rpc_client, &[]).unwrap(), 0);
+        assert_eq!(get_fee_for_messages(&rpc_client, &[]).await.unwrap(), 0);
 
         // One message w/ one signature, a fee.
         let pubkey0 = Pubkey::from([0; 32]);
         let pubkey1 = Pubkey::from([1; 32]);
         let ix0 = system_instruction::transfer(&pubkey0, &pubkey1, 1);
         let message0 = Message::new(&[ix0], Some(&pubkey0));
-        assert_eq!(get_fee_for_messages(&rpc_client, &[&message0]).unwrap(), 1);
+        assert_eq!(
+            get_fee_for_messages(&rpc_client, &[&message0])
+                .await
+                .unwrap(),
+            1
+        );
 
         // No signatures, no fee.
         let check_fee_response = json!(Response {
@@ -313,7 +345,9 @@ mod tests {
         let rpc_client = RpcClient::new_mock_with_mocks("".to_string(), mocks);
         let message = Message::default();
         assert_eq!(
-            get_fee_for_messages(&rpc_client, &[&message, &message]).unwrap(),
+            get_fee_for_messages(&rpc_client, &[&message, &message])
+                .await
+                .unwrap(),
             0
         );
     }

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 458 - 360
cli/src/cli.rs


+ 249 - 141
cli/src/cluster_query.rs

@@ -29,6 +29,9 @@ use {
     },
     solana_clock::{self as clock, Clock, Epoch, Slot},
     solana_commitment_config::CommitmentConfig,
+    solana_connection_cache::connection_cache::{
+        ConnectionManager, ConnectionPool, NewConnectionConfig,
+    },
     solana_hash::Hash,
     solana_message::Message,
     solana_nonce::state::State as NonceState,
@@ -36,7 +39,9 @@ use {
     solana_pubsub_client::pubsub_client::PubsubClient,
     solana_remote_wallet::remote_wallet::RemoteWalletManager,
     solana_rent::Rent,
-    solana_rpc_client::rpc_client::{GetConfirmedSignaturesForAddress2Config, RpcClient},
+    solana_rpc_client::{
+        nonblocking::rpc_client::RpcClient, rpc_client::GetConfirmedSignaturesForAddress2Config,
+    },
     solana_rpc_client_api::{
         client_error::ErrorKind as ClientErrorKind,
         config::{
@@ -53,7 +58,7 @@ use {
     solana_slot_history::{self as slot_history, SlotHistory},
     solana_stake_interface::{self as stake, state::StakeStateV2},
     solana_system_interface::{instruction as system_instruction, MAX_PERMITTED_DATA_LENGTH},
-    solana_tps_client::TpsClient,
+    solana_tpu_client::nonblocking::tpu_client::TpuClient,
     solana_transaction::Transaction,
     solana_transaction_status::{
         EncodableWithMeta, EncodedConfirmedTransactionWithStatusMeta, UiTransactionEncoding,
@@ -725,9 +730,9 @@ pub fn parse_transaction_history(
     ))
 }
 
-pub fn process_catchup(
+pub async fn process_catchup(
     rpc_client: &RpcClient,
-    config: &CliConfig,
+    config: &CliConfig<'_>,
     node_pubkey: Option<Pubkey>,
     mut node_json_rpc_url: Option<String>,
     follow: bool,
@@ -754,7 +759,7 @@ pub fn process_catchup(
 
     let (node_client, node_pubkey) = if our_localhost_port.is_some() {
         let client = RpcClient::new(node_json_rpc_url.unwrap());
-        let guessed_default = Some(client.get_identity()?);
+        let guessed_default = Some(client.get_identity().await?);
         (
             client,
             (if node_pubkey.is_some() && node_pubkey != guessed_default {
@@ -775,7 +780,7 @@ pub fn process_catchup(
             (RpcClient::new(node_json_rpc_url), node_pubkey)
         } else {
             let rpc_addr = loop {
-                let cluster_nodes = rpc_client.get_cluster_nodes()?;
+                let cluster_nodes = rpc_client.get_cluster_nodes().await?;
                 if let Some(contact_info) = cluster_nodes
                     .iter()
                     .find(|contact_info| contact_info.pubkey == node_pubkey.to_string())
@@ -798,7 +803,7 @@ pub fn process_catchup(
     };
 
     let reported_node_pubkey = loop {
-        match node_client.get_identity() {
+        match node_client.get_identity().await {
             Ok(reported_node_pubkey) => break reported_node_pubkey,
             Err(err) => {
                 if let ClientErrorKind::Reqwest(err) = err.kind() {
@@ -819,7 +824,7 @@ pub fn process_catchup(
         .into());
     }
 
-    if rpc_client.get_identity()? == node_pubkey {
+    if rpc_client.get_identity().await? == node_pubkey {
         return Err(
             "Both RPC URLs reference the same node, unable to monitor for catchup.  Try a \
              different --url"
@@ -827,41 +832,80 @@ pub fn process_catchup(
         );
     }
 
-    let mut previous_rpc_slot = i64::MAX;
-    let mut previous_slot_distance: i64 = 0;
-    let mut retry_count: u64 = 0;
-    let max_retry_count = 5;
-    let mut get_slot_while_retrying = |client: &RpcClient| {
+    async fn get_slot_while_retrying(
+        client: &RpcClient,
+        commitment: CommitmentConfig,
+        log: bool,
+        retry_count: &mut u64,
+        max_retry_count: u64,
+    ) -> Result<u64, Box<dyn std::error::Error>> {
         loop {
-            match client.get_slot_with_commitment(config.commitment) {
+            match client.get_slot_with_commitment(commitment).await {
                 Ok(r) => {
-                    retry_count = 0;
+                    *retry_count = 0;
                     return Ok(r);
                 }
                 Err(e) => {
-                    if retry_count >= max_retry_count {
-                        return Err(e);
+                    if *retry_count >= max_retry_count {
+                        return Err(e.into());
                     }
-                    retry_count = retry_count.saturating_add(1);
+                    *retry_count = retry_count.saturating_add(1);
                     if log {
                         // go to new line to leave this message on console
-                        println!("Retrying({retry_count}/{max_retry_count}): {e}\n");
+                        println!("Retrying({}/{max_retry_count}): {e}\n", *retry_count);
                     }
                     sleep(Duration::from_secs(1));
                 }
             };
         }
-    };
+    }
 
-    let start_node_slot: i64 = get_slot_while_retrying(&node_client)?.try_into()?;
-    let start_rpc_slot: i64 = get_slot_while_retrying(rpc_client)?.try_into()?;
+    let mut previous_rpc_slot = i64::MAX;
+    let mut previous_slot_distance: i64 = 0;
+    let mut retry_count: u64 = 0;
+    let max_retry_count = 5;
+
+    let start_node_slot: i64 = get_slot_while_retrying(
+        &node_client,
+        config.commitment,
+        log,
+        &mut retry_count,
+        max_retry_count,
+    )
+    .await?
+    .try_into()?;
+    let start_rpc_slot: i64 = get_slot_while_retrying(
+        rpc_client,
+        config.commitment,
+        log,
+        &mut retry_count,
+        max_retry_count,
+    )
+    .await?
+    .try_into()?;
     let start_slot_distance = start_rpc_slot.saturating_sub(start_node_slot);
     let mut total_sleep_interval = Duration::ZERO;
     loop {
         // humbly retry; the reference node (rpc_client) could be spotty,
         // especially if pointing to api.meinnet-beta.solana.com at times
-        let rpc_slot: i64 = get_slot_while_retrying(rpc_client)?.try_into()?;
-        let node_slot: i64 = get_slot_while_retrying(&node_client)?.try_into()?;
+        let rpc_slot: i64 = get_slot_while_retrying(
+            rpc_client,
+            config.commitment,
+            log,
+            &mut retry_count,
+            max_retry_count,
+        )
+        .await?
+        .try_into()?;
+        let node_slot: i64 = get_slot_while_retrying(
+            &node_client,
+            config.commitment,
+            log,
+            &mut retry_count,
+            max_retry_count,
+        )
+        .await?
+        .try_into()?;
         if !follow && node_slot > std::cmp::min(previous_rpc_slot, rpc_slot) {
             progress_bar.finish_and_clear();
             return Ok(format!(
@@ -939,8 +983,10 @@ pub fn process_catchup(
     }
 }
 
-pub fn process_cluster_date(rpc_client: &RpcClient, config: &CliConfig) -> ProcessResult {
-    let result = rpc_client.get_account_with_commitment(&sysvar::clock::id(), config.commitment)?;
+pub async fn process_cluster_date(rpc_client: &RpcClient, config: &CliConfig<'_>) -> ProcessResult {
+    let result = rpc_client
+        .get_account_with_commitment(&sysvar::clock::id(), config.commitment)
+        .await?;
     if let Some(clock_account) = result.value {
         let clock: Clock = from_account(&clock_account).ok_or_else(|| {
             CliError::RpcRequestError("Failed to deserialize clock sysvar".to_string())
@@ -955,8 +1001,11 @@ pub fn process_cluster_date(rpc_client: &RpcClient, config: &CliConfig) -> Proce
     }
 }
 
-pub fn process_cluster_version(rpc_client: &RpcClient, config: &CliConfig) -> ProcessResult {
-    let remote_version = rpc_client.get_version()?;
+pub async fn process_cluster_version(
+    rpc_client: &RpcClient,
+    config: &CliConfig<'_>,
+) -> ProcessResult {
+    let remote_version = rpc_client.get_version().await?;
 
     if config.verbose {
         Ok(format!("{remote_version:?}"))
@@ -965,8 +1014,8 @@ pub fn process_cluster_version(rpc_client: &RpcClient, config: &CliConfig) -> Pr
     }
 }
 
-pub fn process_first_available_block(rpc_client: &RpcClient) -> ProcessResult {
-    let first_available_block = rpc_client.get_first_available_block()?;
+pub async fn process_first_available_block(rpc_client: &RpcClient) -> ProcessResult {
+    let first_available_block = rpc_client.get_first_available_block().await?;
     Ok(format!("{first_available_block}"))
 }
 
@@ -977,21 +1026,23 @@ pub fn parse_leader_schedule(matches: &ArgMatches<'_>) -> Result<CliCommandInfo,
     ))
 }
 
-pub fn process_leader_schedule(
+pub async fn process_leader_schedule(
     rpc_client: &RpcClient,
-    config: &CliConfig,
+    config: &CliConfig<'_>,
     epoch: Option<Epoch>,
 ) -> ProcessResult {
-    let epoch_info = rpc_client.get_epoch_info()?;
+    let epoch_info = rpc_client.get_epoch_info().await?;
     let epoch = epoch.unwrap_or(epoch_info.epoch);
     if epoch > epoch_info.epoch.saturating_add(1) {
         return Err(format!("Epoch {epoch} is more than one epoch in the future").into());
     }
 
-    let epoch_schedule = rpc_client.get_epoch_schedule()?;
+    let epoch_schedule = rpc_client.get_epoch_schedule().await?;
     let first_slot_in_epoch = epoch_schedule.get_first_slot_in_epoch(epoch);
 
-    let leader_schedule = rpc_client.get_leader_schedule(Some(first_slot_in_epoch))?;
+    let leader_schedule = rpc_client
+        .get_leader_schedule(Some(first_slot_in_epoch))
+        .await?;
     if leader_schedule.is_none() {
         return Err(
             format!("Unable to fetch leader schedule for slot {first_slot_in_epoch}").into(),
@@ -1023,13 +1074,13 @@ pub fn process_leader_schedule(
     }))
 }
 
-pub fn process_get_recent_priority_fees(
+pub async fn process_get_recent_priority_fees(
     rpc_client: &RpcClient,
-    config: &CliConfig,
+    config: &CliConfig<'_>,
     accounts: &[Pubkey],
     limit_num_slots: Option<Slot>,
 ) -> ProcessResult {
-    let fees = rpc_client.get_recent_prioritization_fees(accounts)?;
+    let fees = rpc_client.get_recent_prioritization_fees(accounts).await?;
     let mut min = u64::MAX;
     let mut max = 0;
     let mut total = Saturating(0);
@@ -1063,15 +1114,17 @@ pub fn process_get_recent_priority_fees(
         }))
 }
 
-pub fn process_get_block(
+pub async fn process_get_block(
     rpc_client: &RpcClient,
-    config: &CliConfig,
+    config: &CliConfig<'_>,
     slot: Option<Slot>,
 ) -> ProcessResult {
     let slot = if let Some(slot) = slot {
         slot
     } else {
-        rpc_client.get_slot_with_commitment(CommitmentConfig::finalized())?
+        rpc_client
+            .get_slot_with_commitment(CommitmentConfig::finalized())
+            .await?
     };
 
     let encoded_confirmed_block = rpc_client
@@ -1083,7 +1136,8 @@ pub fn process_get_block(
                 max_supported_transaction_version: Some(0),
                 ..RpcBlockConfig::default()
             },
-        )?
+        )
+        .await?
         .into();
     let cli_block = CliBlock {
         encoded_confirmed_block,
@@ -1092,28 +1146,33 @@ pub fn process_get_block(
     Ok(config.output_format.formatted_string(&cli_block))
 }
 
-pub fn process_get_block_time(
+pub async fn process_get_block_time(
     rpc_client: &RpcClient,
-    config: &CliConfig,
+    config: &CliConfig<'_>,
     slot: Option<Slot>,
 ) -> ProcessResult {
     let slot = if let Some(slot) = slot {
         slot
     } else {
-        rpc_client.get_slot_with_commitment(CommitmentConfig::finalized())?
+        rpc_client
+            .get_slot_with_commitment(CommitmentConfig::finalized())
+            .await?
     };
-    let timestamp = rpc_client.get_block_time(slot)?;
+    let timestamp = rpc_client.get_block_time(slot).await?;
     let block_time = CliBlockTime { slot, timestamp };
     Ok(config.output_format.formatted_string(&block_time))
 }
 
-pub fn process_get_epoch(rpc_client: &RpcClient, _config: &CliConfig) -> ProcessResult {
-    let epoch_info = rpc_client.get_epoch_info()?;
+pub async fn process_get_epoch(rpc_client: &RpcClient, _config: &CliConfig<'_>) -> ProcessResult {
+    let epoch_info = rpc_client.get_epoch_info().await?;
     Ok(epoch_info.epoch.to_string())
 }
 
-pub fn process_get_epoch_info(rpc_client: &RpcClient, config: &CliConfig) -> ProcessResult {
-    let epoch_info = rpc_client.get_epoch_info()?;
+pub async fn process_get_epoch_info(
+    rpc_client: &RpcClient,
+    config: &CliConfig<'_>,
+) -> ProcessResult {
+    let epoch_info = rpc_client.get_epoch_info().await?;
     let epoch_completed_percent =
         epoch_info.slot_index as f64 / epoch_info.slots_in_epoch as f64 * 100_f64;
     let mut cli_epoch_info = CliEpochInfo {
@@ -1129,6 +1188,7 @@ pub fn process_get_epoch_info(rpc_client: &RpcClient, config: &CliConfig) -> Pro
             let epoch_info = &cli_epoch_info.epoch_info;
             let average_slot_time_ms = rpc_client
                 .get_recent_performance_samples(Some(60))
+                .await
                 .ok()
                 .and_then(|samples| {
                     let (slots, secs) = samples.iter().fold(
@@ -1153,22 +1213,26 @@ pub fn process_get_epoch_info(rpc_client: &RpcClient, config: &CliConfig) -> Pro
                 .saturating_sub(epoch_info.slot_index);
             let first_block_in_epoch = rpc_client
                 .get_blocks_with_limit(epoch_expected_start_slot, 1)
+                .await
                 .ok()
                 .and_then(|slot_vec| slot_vec.first().cloned())
                 .unwrap_or(epoch_expected_start_slot);
-            let start_block_time =
-                rpc_client
-                    .get_block_time(first_block_in_epoch)
-                    .ok()
-                    .map(|time| {
-                        time.saturating_sub(
-                            first_block_in_epoch
-                                .saturating_sub(epoch_expected_start_slot)
-                                .saturating_mul(average_slot_time_ms)
-                                .saturating_div(1000) as i64,
-                        )
-                    });
-            let current_block_time = rpc_client.get_block_time(epoch_info.absolute_slot).ok();
+            let start_block_time = rpc_client
+                .get_block_time(first_block_in_epoch)
+                .await
+                .ok()
+                .map(|time| {
+                    time.saturating_sub(
+                        first_block_in_epoch
+                            .saturating_sub(epoch_expected_start_slot)
+                            .saturating_mul(average_slot_time_ms)
+                            .saturating_div(1000) as i64,
+                    )
+                });
+            let current_block_time = rpc_client
+                .get_block_time(epoch_info.absolute_slot)
+                .await
+                .ok();
 
             cli_epoch_info.average_slot_time_ms = average_slot_time_ms;
             cli_epoch_info.start_block_time = start_block_time;
@@ -1178,18 +1242,21 @@ pub fn process_get_epoch_info(rpc_client: &RpcClient, config: &CliConfig) -> Pro
     Ok(config.output_format.formatted_string(&cli_epoch_info))
 }
 
-pub fn process_get_genesis_hash(rpc_client: &RpcClient) -> ProcessResult {
-    let genesis_hash = rpc_client.get_genesis_hash()?;
+pub async fn process_get_genesis_hash(rpc_client: &RpcClient) -> ProcessResult {
+    let genesis_hash = rpc_client.get_genesis_hash().await?;
     Ok(genesis_hash.to_string())
 }
 
-pub fn process_get_slot(rpc_client: &RpcClient, _config: &CliConfig) -> ProcessResult {
-    let slot = rpc_client.get_slot()?;
+pub async fn process_get_slot(rpc_client: &RpcClient, _config: &CliConfig<'_>) -> ProcessResult {
+    let slot = rpc_client.get_slot().await?;
     Ok(slot.to_string())
 }
 
-pub fn process_get_block_height(rpc_client: &RpcClient, _config: &CliConfig) -> ProcessResult {
-    let block_height = rpc_client.get_block_height()?;
+pub async fn process_get_block_height(
+    rpc_client: &RpcClient,
+    _config: &CliConfig<'_>,
+) -> ProcessResult {
+    let block_height = rpc_client.get_block_height().await?;
     Ok(block_height.to_string())
 }
 
@@ -1202,14 +1269,16 @@ pub fn parse_show_block_production(matches: &ArgMatches<'_>) -> Result<CliComman
     ))
 }
 
-pub fn process_show_block_production(
+pub async fn process_show_block_production(
     rpc_client: &RpcClient,
-    config: &CliConfig,
+    config: &CliConfig<'_>,
     epoch: Option<Epoch>,
     slot_limit: Option<u64>,
 ) -> ProcessResult {
-    let epoch_schedule = rpc_client.get_epoch_schedule()?;
-    let epoch_info = rpc_client.get_epoch_info_with_commitment(CommitmentConfig::finalized())?;
+    let epoch_schedule = rpc_client.get_epoch_schedule().await?;
+    let epoch_info = rpc_client
+        .get_epoch_info_with_commitment(CommitmentConfig::finalized())
+        .await?;
 
     let epoch = epoch.unwrap_or(epoch_info.epoch);
     if epoch > epoch_info.epoch {
@@ -1234,7 +1303,8 @@ pub fn process_show_block_production(
     ));
 
     let slot_history_account = rpc_client
-        .get_account_with_commitment(&sysvar::slot_history::id(), CommitmentConfig::finalized())?
+        .get_account_with_commitment(&sysvar::slot_history::id(), CommitmentConfig::finalized())
+        .await?
         .value
         .unwrap();
 
@@ -1257,7 +1327,7 @@ pub fn process_show_block_production(
             // incorrect.  This condition currently can't be detected over RPC
             //
 
-            let minimum_ledger_slot = rpc_client.minimum_ledger_slot()?;
+            let minimum_ledger_slot = rpc_client.minimum_ledger_slot().await?;
             if minimum_ledger_slot > end_slot {
                 return Err(format!(
                     "Ledger data not available for slots {start_slot} to {end_slot} (minimum \
@@ -1278,7 +1348,7 @@ pub fn process_show_block_production(
                 start_slot = minimum_ledger_slot;
             }
 
-            let confirmed_blocks = rpc_client.get_blocks(start_slot, Some(end_slot))?;
+            let confirmed_blocks = rpc_client.get_blocks(start_slot, Some(end_slot)).await?;
             (confirmed_blocks, start_slot)
         };
 
@@ -1295,7 +1365,8 @@ pub fn process_show_block_production(
 
     progress_bar.set_message(format!("Fetching leader schedule for epoch {epoch}..."));
     let leader_schedule = rpc_client
-        .get_leader_schedule_with_commitment(Some(start_slot), CommitmentConfig::finalized())?;
+        .get_leader_schedule_with_commitment(Some(start_slot), CommitmentConfig::finalized())
+        .await?;
     if leader_schedule.is_none() {
         return Err(format!("Unable to fetch leader schedule for slot {start_slot}").into());
     }
@@ -1382,9 +1453,9 @@ pub fn process_show_block_production(
     Ok(config.output_format.formatted_string(&block_production))
 }
 
-pub fn process_largest_accounts(
+pub async fn process_largest_accounts(
     rpc_client: &RpcClient,
-    config: &CliConfig,
+    config: &CliConfig<'_>,
     filter: Option<RpcLargestAccountsFilter>,
 ) -> ProcessResult {
     let accounts = rpc_client
@@ -1392,39 +1463,46 @@ pub fn process_largest_accounts(
             commitment: Some(config.commitment),
             filter,
             sort_results: None,
-        })?
+        })
+        .await?
         .value;
     let largest_accounts = CliAccountBalances { accounts };
     Ok(config.output_format.formatted_string(&largest_accounts))
 }
 
-pub fn process_supply(
+pub async fn process_supply(
     rpc_client: &RpcClient,
-    config: &CliConfig,
+    config: &CliConfig<'_>,
     print_accounts: bool,
 ) -> ProcessResult {
-    let supply_response = rpc_client.supply()?;
+    let supply_response = rpc_client.supply().await?;
     let mut supply: CliSupply = supply_response.value.into();
     supply.print_accounts = print_accounts;
     Ok(config.output_format.formatted_string(&supply))
 }
 
-pub fn process_total_supply(rpc_client: &RpcClient, _config: &CliConfig) -> ProcessResult {
-    let supply = rpc_client.supply()?.value;
+pub async fn process_total_supply(
+    rpc_client: &RpcClient,
+    _config: &CliConfig<'_>,
+) -> ProcessResult {
+    let supply = rpc_client.supply().await?.value;
     Ok(format!(
         "{} SOL",
         build_balance_message(supply.total, false, false)
     ))
 }
 
-pub fn process_get_transaction_count(rpc_client: &RpcClient, _config: &CliConfig) -> ProcessResult {
-    let transaction_count = rpc_client.get_transaction_count()?;
+pub async fn process_get_transaction_count(
+    rpc_client: &RpcClient,
+    _config: &CliConfig<'_>,
+) -> ProcessResult {
+    let transaction_count = rpc_client.get_transaction_count().await?;
     Ok(transaction_count.to_string())
 }
 
-pub fn process_ping(
-    tps_client: &Arc<dyn TpsClient>,
-    config: &CliConfig,
+pub async fn process_ping<P, M, C>(
+    tpu_client: Option<&TpuClient<P, M, C>>,
+    config: &CliConfig<'_>,
     interval: &Duration,
     count: &Option<u64>,
     timeout: &Duration,
@@ -1432,7 +1510,12 @@ pub fn process_ping(
     print_timestamp: bool,
     compute_unit_price: Option<u64>,
     rpc_client: &RpcClient,
-) -> ProcessResult {
+) -> ProcessResult
+where
+    P: ConnectionPool<NewConnectionConfig = C>,
+    M: ConnectionManager<ConnectionPool = P, NewConnectionConfig = C>,
+    C: NewConnectionConfig,
+{
     let (signal_sender, signal_receiver) = unbounded();
     let handler = move || {
         let _ = signal_sender.send(());
@@ -1450,7 +1533,7 @@ pub fn process_ping(
     let mut confirmed_count: u32 = 0;
     let mut confirmation_time: VecDeque<u64> = VecDeque::with_capacity(1024);
 
-    let mut blockhash = tps_client.get_latest_blockhash()?;
+    let mut blockhash = rpc_client.get_latest_blockhash().await?;
     let mut lamports: u64 = 0;
     let mut blockhash_acquired = Instant::now();
     let mut blockhash_from_cluster = false;
@@ -1474,7 +1557,7 @@ pub fn process_ping(
             compute_unit_limit: ComputeUnitLimit::Simulated,
         });
         let message = Message::new(&ixs, Some(&config.signers[0].pubkey()));
-        ComputeUnitLimit::Static(simulate_for_compute_unit_limit(rpc_client, &message)?)
+        ComputeUnitLimit::Static(simulate_for_compute_unit_limit(rpc_client, &message).await?)
     } else {
         ComputeUnitLimit::Default
     };
@@ -1483,7 +1566,7 @@ pub fn process_ping(
         let now = Instant::now();
         if fixed_blockhash.is_none() && now.duration_since(blockhash_acquired).as_secs() > 60 {
             // Fetch a new blockhash every minute
-            let new_blockhash = tps_client.get_new_latest_blockhash(&blockhash)?;
+            let new_blockhash = rpc_client.get_new_latest_blockhash(&blockhash).await?;
             blockhash = new_blockhash;
             lamports = 0;
             blockhash_acquired = Instant::now();
@@ -1512,7 +1595,8 @@ pub fn process_ping(
             compute_unit_limit,
             build_message,
             config.commitment,
-        )?;
+        )
+        .await?;
         let mut tx = Transaction::new_unsigned(message);
         tx.try_sign(&config.signers, blockhash)?;
 
@@ -1524,11 +1608,23 @@ pub fn process_ping(
             format!("[{}.{:06}] ", micros / 1_000_000, micros % 1_000_000)
         };
 
-        match tps_client.send_transaction(tx) {
+        let send_result = if let Some(tpu_client) = tpu_client {
+            match tpu_client.try_send_transaction(&tx).await {
+                Ok(()) => Ok(*tx.signatures.first().unwrap()),
+                Err(err) => Err(format!("TPU send error: {err}")),
+            }
+        } else {
+            rpc_client
+                .send_transaction(&tx)
+                .await
+                .map_err(|err| err.to_string())
+        };
+
+        match send_result {
             Ok(signature) => {
                 let transaction_sent = Instant::now();
                 loop {
-                    let signature_status = tps_client.get_signature_status(&signature)?;
+                    let signature_status = rpc_client.get_signature_status(&signature).await?;
                     let elapsed_time = Instant::now().duration_since(transaction_sent);
                     if let Some(transaction_status) = signature_status {
                         match transaction_status {
@@ -1793,8 +1889,8 @@ pub fn process_live_slots(config: &CliConfig) -> ProcessResult {
     Ok("".to_string())
 }
 
-pub fn process_show_gossip(rpc_client: &RpcClient, config: &CliConfig) -> ProcessResult {
-    let cluster_nodes = rpc_client.get_cluster_nodes()?;
+pub async fn process_show_gossip(rpc_client: &RpcClient, config: &CliConfig<'_>) -> ProcessResult {
+    let cluster_nodes = rpc_client.get_cluster_nodes().await?;
 
     let nodes: Vec<_> = cluster_nodes
         .into_iter()
@@ -1806,9 +1902,9 @@ pub fn process_show_gossip(rpc_client: &RpcClient, config: &CliConfig) -> Proces
         .formatted_string(&CliGossipNodes(nodes)))
 }
 
-pub fn process_show_stakes(
+pub async fn process_show_stakes(
     rpc_client: &RpcClient,
-    config: &CliConfig,
+    config: &CliConfig<'_>,
     use_lamports_unit: bool,
     vote_account_pubkeys: Option<&[Pubkey]>,
     withdraw_authority_pubkey: Option<&Pubkey>,
@@ -1822,7 +1918,7 @@ pub fn process_show_stakes(
             let vote_account_progress_bar = new_spinner_progress_bar();
             vote_account_progress_bar.set_message("Searching for matching vote accounts...");
 
-            let vote_accounts = rpc_client.get_vote_accounts()?;
+            let vote_accounts = rpc_client.get_vote_accounts().await?;
 
             let mut pubkeys: HashSet<String> =
                 pubkeys.iter().map(|pubkey| pubkey.to_string()).collect();
@@ -1886,9 +1982,10 @@ pub fn process_show_stakes(
     }
 
     let all_stake_accounts = rpc_client
-        .get_program_ui_accounts_with_config(&stake::program::id(), program_accounts_config)?;
-    let stake_history_account = rpc_client.get_account(&stake_history::id())?;
-    let clock_account = rpc_client.get_account(&sysvar::clock::id())?;
+        .get_program_ui_accounts_with_config(&stake::program::id(), program_accounts_config)
+        .await?;
+    let stake_history_account = rpc_client.get_account(&stake_history::id()).await?;
+    let clock_account = rpc_client.get_account(&sysvar::clock::id()).await?;
     let clock: Clock = from_account(&clock_account).ok_or_else(|| {
         CliError::RpcRequestError("Failed to deserialize clock sysvar".to_string())
     })?;
@@ -1898,7 +1995,8 @@ pub fn process_show_stakes(
     let new_rate_activation_epoch = get_feature_activation_epoch(
         rpc_client,
         &agave_feature_set::reduce_stake_warmup_cooldown::id(),
-    )?;
+    )
+    .await?;
     stake_account_progress_bar.finish_and_clear();
 
     let mut stake_accounts: Vec<CliKeyedStakeState> = vec![];
@@ -1956,19 +2054,21 @@ pub fn process_show_stakes(
     }
 }
 
-pub fn process_wait_for_max_stake(
+pub async fn process_wait_for_max_stake(
     rpc_client: &RpcClient,
-    config: &CliConfig,
+    config: &CliConfig<'_>,
     max_stake_percent: f32,
 ) -> ProcessResult {
     let now = std::time::Instant::now();
-    rpc_client.wait_for_max_stake(config.commitment, max_stake_percent)?;
+    rpc_client
+        .wait_for_max_stake(config.commitment, max_stake_percent)
+        .await?;
     Ok(format!("Done waiting, took: {}s", now.elapsed().as_secs()))
 }
 
-pub fn process_show_validators(
+pub async fn process_show_validators(
     rpc_client: &RpcClient,
-    config: &CliConfig,
+    config: &CliConfig<'_>,
     use_lamports_unit: bool,
     validators_sort_order: CliValidatorsSortOrder,
     validators_reverse_sort: bool,
@@ -1978,16 +2078,19 @@ pub fn process_show_validators(
 ) -> ProcessResult {
     let progress_bar = new_spinner_progress_bar();
     progress_bar.set_message("Fetching vote accounts...");
-    let epoch_info = rpc_client.get_epoch_info()?;
-    let vote_accounts = rpc_client.get_vote_accounts_with_config(RpcGetVoteAccountsConfig {
-        keep_unstaked_delinquents: Some(keep_unstaked_delinquents),
-        delinquent_slot_distance,
-        ..RpcGetVoteAccountsConfig::default()
-    })?;
+    let epoch_info = rpc_client.get_epoch_info().await?;
+    let vote_accounts = rpc_client
+        .get_vote_accounts_with_config(RpcGetVoteAccountsConfig {
+            keep_unstaked_delinquents: Some(keep_unstaked_delinquents),
+            delinquent_slot_distance,
+            ..RpcGetVoteAccountsConfig::default()
+        })
+        .await?;
 
     progress_bar.set_message("Fetching block production...");
     let skip_rate: HashMap<_, _> = rpc_client
-        .get_block_production()?
+        .get_block_production()
+        .await?
         .value
         .by_identity
         .into_iter()
@@ -2001,7 +2104,7 @@ pub fn process_show_validators(
 
     progress_bar.set_message("Fetching version information...");
     let mut node_version = HashMap::new();
-    for contact_info in rpc_client.get_cluster_nodes()? {
+    for contact_info in rpc_client.get_cluster_nodes().await? {
         node_version.insert(
             contact_info.pubkey,
             contact_info
@@ -2128,24 +2231,26 @@ pub fn process_show_validators(
     Ok(config.output_format.formatted_string(&cli_validators))
 }
 
-pub fn process_transaction_history(
+pub async fn process_transaction_history(
     rpc_client: &RpcClient,
-    config: &CliConfig,
+    config: &CliConfig<'_>,
     address: &Pubkey,
     before: Option<Signature>,
     until: Option<Signature>,
     limit: usize,
     show_transactions: bool,
 ) -> ProcessResult {
-    let results = rpc_client.get_signatures_for_address_with_config(
-        address,
-        GetConfirmedSignaturesForAddress2Config {
-            before,
-            until,
-            limit: Some(limit),
-            commitment: Some(CommitmentConfig::confirmed()),
-        },
-    )?;
+    let results = rpc_client
+        .get_signatures_for_address_with_config(
+            address,
+            GetConfirmedSignaturesForAddress2Config {
+                before,
+                until,
+                limit: Some(limit),
+                commitment: Some(CommitmentConfig::confirmed()),
+            },
+        )
+        .await?;
 
     if !show_transactions {
         let cli_signatures: Vec<_> = results
@@ -2176,14 +2281,17 @@ pub fn process_transaction_history(
             if let Ok(signature) = result.signature.parse::<Signature>() {
                 let mut transaction = None;
                 let mut get_transaction_error = None;
-                match rpc_client.get_transaction_with_config(
-                    &signature,
-                    RpcTransactionConfig {
-                        encoding: Some(UiTransactionEncoding::Base64),
-                        commitment: Some(CommitmentConfig::confirmed()),
-                        max_supported_transaction_version: Some(0),
-                    },
-                ) {
+                match rpc_client
+                    .get_transaction_with_config(
+                        &signature,
+                        RpcTransactionConfig {
+                            encoding: Some(UiTransactionEncoding::Base64),
+                            commitment: Some(CommitmentConfig::confirmed()),
+                            max_supported_transaction_version: Some(0),
+                        },
+                    )
+                    .await
+                {
                     Ok(confirmed_transaction) => {
                         let EncodedConfirmedTransactionWithStatusMeta {
                             block_time,
@@ -2292,9 +2400,9 @@ impl FromStr for RentLengthValue {
     }
 }
 
-pub fn process_calculate_rent(
+pub async fn process_calculate_rent(
     rpc_client: &RpcClient,
-    config: &CliConfig,
+    config: &CliConfig<'_>,
     data_length: usize,
     use_lamports_unit: bool,
 ) -> ProcessResult {
@@ -2304,7 +2412,7 @@ pub fn process_calculate_rent(
              provided"
         );
     }
-    let rent_account = rpc_client.get_account(&sysvar::rent::id())?;
+    let rent_account = rpc_client.get_account(&sysvar::rent::id()).await?;
     let rent: Rent = rent_account.deserialize_data()?;
     let rent_exempt_minimum_lamports = rent.minimum_balance(data_length);
     let cli_rent_calculation = CliRentCalculation {

+ 8 - 7
cli/src/compute_budget.rs

@@ -5,7 +5,7 @@ use {
     solana_instruction::Instruction,
     solana_message::Message,
     solana_program_runtime::execution_budget::MAX_COMPUTE_UNIT_LIMIT,
-    solana_rpc_client::rpc_client::RpcClient,
+    solana_rpc_client::nonblocking::rpc_client::RpcClient,
     solana_rpc_client_api::config::RpcSimulateTransactionConfig,
     solana_transaction::Transaction,
 };
@@ -39,7 +39,7 @@ fn get_compute_unit_limit_instruction_index(message: &Message) -> Option<usize>
 
 /// Like `simulate_for_compute_unit_limit`, but does not check that the message
 /// contains a compute unit limit instruction.
-fn simulate_for_compute_unit_limit_unchecked(
+async fn simulate_for_compute_unit_limit_unchecked(
     rpc_client: &RpcClient,
     message: &Message,
 ) -> Result<u32, Box<dyn std::error::Error>> {
@@ -52,7 +52,8 @@ fn simulate_for_compute_unit_limit_unchecked(
                 commitment: Some(rpc_client.commitment()),
                 ..RpcSimulateTransactionConfig::default()
             },
-        )?
+        )
+        .await?
         .value;
 
     // Bail if the simulated transaction failed
@@ -71,14 +72,14 @@ fn simulate_for_compute_unit_limit_unchecked(
 ///
 /// Returns an error if the message does not contain a compute unit limit
 /// instruction or if the simulation fails.
-pub(crate) fn simulate_for_compute_unit_limit(
+pub(crate) async fn simulate_for_compute_unit_limit(
     rpc_client: &RpcClient,
     message: &Message,
 ) -> Result<u32, Box<dyn std::error::Error>> {
     if get_compute_unit_limit_instruction_index(message).is_none() {
         return Err("No compute unit limit instruction found".into());
     }
-    simulate_for_compute_unit_limit_unchecked(rpc_client, message)
+    simulate_for_compute_unit_limit_unchecked(rpc_client, message).await
 }
 
 /// Simulates a message and returns the index of the compute unit limit
@@ -87,7 +88,7 @@ pub(crate) fn simulate_for_compute_unit_limit(
 /// If the message does not contain a compute unit limit instruction, or if
 /// simulation was not configured, then the function will not simulate the
 /// message.
-pub(crate) fn simulate_and_update_compute_unit_limit(
+pub(crate) async fn simulate_and_update_compute_unit_limit(
     compute_unit_limit: &ComputeUnitLimit,
     rpc_client: &RpcClient,
     message: &mut Message,
@@ -100,7 +101,7 @@ pub(crate) fn simulate_and_update_compute_unit_limit(
     match compute_unit_limit {
         ComputeUnitLimit::Simulated | ComputeUnitLimit::SimulatedWithExtraPercentage(_) => {
             let base_compute_unit_limit =
-                simulate_for_compute_unit_limit_unchecked(rpc_client, message)?;
+                simulate_for_compute_unit_limit_unchecked(rpc_client, message).await?;
 
             let compute_unit_limit =
                 if let ComputeUnitLimit::SimulatedWithExtraPercentage(n) = compute_unit_limit {

+ 67 - 56
cli/src/feature.rs

@@ -26,7 +26,7 @@ use {
     solana_message::Message,
     solana_pubkey::Pubkey,
     solana_remote_wallet::remote_wallet::RemoteWalletManager,
-    solana_rpc_client::rpc_client::RpcClient,
+    solana_rpc_client::nonblocking::rpc_client::RpcClient,
     solana_rpc_client_api::{
         client_error::Error as ClientError, request::MAX_MULTIPLE_ACCOUNTS,
         response::RpcVoteAccountInfo,
@@ -415,12 +415,12 @@ pub struct CliSoftwareVersionStats {
 }
 
 /// Check an RPC's reported genesis hash against the ClusterType's known genesis hash
-fn check_rpc_genesis_hash(
+async fn check_rpc_genesis_hash(
     cluster_type: &ClusterType,
     rpc_client: &RpcClient,
 ) -> Result<(), Box<dyn std::error::Error>> {
     if let Some(genesis_hash) = cluster_type.get_genesis_hash() {
-        let rpc_genesis_hash = rpc_client.get_genesis_hash()?;
+        let rpc_genesis_hash = rpc_client.get_genesis_hash().await?;
         if rpc_genesis_hash != genesis_hash {
             return Err(format!(
                 "The genesis hash for the specified cluster {cluster_type:?} does not match the \
@@ -606,27 +606,27 @@ pub fn parse_feature_subcommand(
     Ok(response)
 }
 
-pub fn process_feature_subcommand(
+pub async fn process_feature_subcommand(
     rpc_client: &RpcClient,
-    config: &CliConfig,
+    config: &CliConfig<'_>,
     feature_subcommand: &FeatureCliCommand,
 ) -> ProcessResult {
     match feature_subcommand {
         FeatureCliCommand::Status {
             features,
             display_all,
-        } => process_status(rpc_client, config, features, *display_all),
+        } => process_status(rpc_client, config, features, *display_all).await,
         FeatureCliCommand::Activate {
             feature,
             cluster,
             force,
             fee_payer,
-        } => process_activate(rpc_client, config, *feature, *cluster, *force, *fee_payer),
+        } => process_activate(rpc_client, config, *feature, *cluster, *force, *fee_payer).await,
         FeatureCliCommand::Revoke {
             feature,
             cluster,
             fee_payer,
-        } => process_revoke(rpc_client, config, *feature, *cluster, *fee_payer),
+        } => process_revoke(rpc_client, config, *feature, *cluster, *fee_payer).await,
     }
 }
 
@@ -677,7 +677,7 @@ impl ClusterInfoStats {
     }
 }
 
-fn cluster_info_stats(rpc_client: &RpcClient) -> Result<ClusterInfoStats, ClientError> {
+async fn cluster_info_stats(rpc_client: &RpcClient) -> Result<ClusterInfoStats, ClientError> {
     #[derive(Default)]
     struct StatsEntry {
         stake_lamports: u64,
@@ -685,7 +685,8 @@ fn cluster_info_stats(rpc_client: &RpcClient) -> Result<ClusterInfoStats, Client
     }
 
     let cluster_info_list = rpc_client
-        .get_cluster_nodes()?
+        .get_cluster_nodes()
+        .await?
         .into_iter()
         .map(|contact_info| {
             (
@@ -700,7 +701,7 @@ fn cluster_info_stats(rpc_client: &RpcClient) -> Result<ClusterInfoStats, Client
         })
         .collect::<Vec<_>>();
 
-    let vote_accounts = rpc_client.get_vote_accounts()?;
+    let vote_accounts = rpc_client.get_vote_accounts().await?;
 
     let mut total_active_stake: u64 = vote_accounts
         .delinquent
@@ -775,7 +776,7 @@ fn cluster_info_stats(rpc_client: &RpcClient) -> Result<ClusterInfoStats, Client
 }
 
 // Feature activation is only allowed when 95% of the active stake is on the current feature set
-fn feature_activation_allowed(
+async fn feature_activation_allowed(
     rpc_client: &RpcClient,
     quiet: bool,
 ) -> Result<
@@ -786,7 +787,7 @@ fn feature_activation_allowed(
     ),
     ClientError,
 > {
-    let cluster_info_stats = cluster_info_stats(rpc_client)?;
+    let cluster_info_stats = cluster_info_stats(rpc_client).await?;
     let feature_set_stats = cluster_info_stats.aggregate_by_feature_set();
 
     let tool_version = solana_version::Version::default();
@@ -882,47 +883,47 @@ pub(super) fn status_from_account(account: Account) -> Option<CliFeatureStatus>
     })
 }
 
-fn get_feature_status(
+async fn get_feature_status(
     rpc_client: &RpcClient,
     feature_id: &Pubkey,
 ) -> Result<Option<CliFeatureStatus>, Box<dyn std::error::Error>> {
     rpc_client
         .get_account(feature_id)
+        .await
         .map(status_from_account)
         .map_err(|e| e.into())
 }
 
-pub fn get_feature_is_active(
+pub async fn get_feature_is_active(
     rpc_client: &RpcClient,
     feature_id: &Pubkey,
 ) -> Result<bool, Box<dyn std::error::Error>> {
     get_feature_status(rpc_client, feature_id)
+        .await
         .map(|status| matches!(status, Some(CliFeatureStatus::Active(_))))
 }
 
-pub fn get_feature_activation_epoch(
+pub async fn get_feature_activation_epoch(
     rpc_client: &RpcClient,
     feature_id: &Pubkey,
 ) -> Result<Option<Epoch>, ClientError> {
-    rpc_client
-        .get_feature_activation_slot(feature_id)
-        .and_then(|activation_slot: Option<Slot>| {
-            rpc_client
-                .get_epoch_schedule()
-                .map(|epoch_schedule| (activation_slot, epoch_schedule))
-        })
-        .map(|(activation_slot, epoch_schedule)| {
-            activation_slot.map(|slot| epoch_schedule.get_epoch(slot))
-        })
+    let activation_slot = rpc_client
+        .get_account(feature_id)
+        .await
+        .ok()
+        .and_then(|account| from_account(&account))
+        .and_then(|feature| feature.activated_at);
+    let epoch_schedule = rpc_client.get_epoch_schedule().await?;
+    Ok(activation_slot.map(|slot| epoch_schedule.get_epoch(slot)))
 }
 
-fn process_status(
+async fn process_status(
     rpc_client: &RpcClient,
-    config: &CliConfig,
+    config: &CliConfig<'_>,
     feature_ids: &[Pubkey],
     display_all: bool,
 ) -> ProcessResult {
-    let current_slot = rpc_client.get_slot()?;
+    let current_slot = rpc_client.get_slot().await?;
     let filter = if !display_all {
         current_slot.checked_sub(DEFAULT_MAX_ACTIVE_DISPLAY_AGE_SLOTS)
     } else {
@@ -932,7 +933,8 @@ fn process_status(
     let mut features = vec![];
     for feature_ids in feature_ids.chunks(MAX_MULTIPLE_ACCOUNTS) {
         let mut feature_chunk = rpc_client
-            .get_multiple_accounts(feature_ids)?
+            .get_multiple_accounts(feature_ids)
+            .await?
             .into_iter()
             .zip(feature_ids)
             .map(|(account, feature_id)| {
@@ -966,8 +968,8 @@ fn process_status(
     features.sort_unstable();
 
     let (feature_activation_allowed, cluster_feature_sets, cluster_software_versions) =
-        feature_activation_allowed(rpc_client, features.len() <= 1)?;
-    let epoch_schedule = rpc_client.get_epoch_schedule()?;
+        feature_activation_allowed(rpc_client, features.len() <= 1).await?;
+    let epoch_schedule = rpc_client.get_epoch_schedule().await?;
     let feature_set = CliFeatures {
         features,
         current_slot,
@@ -980,19 +982,20 @@ fn process_status(
     Ok(config.output_format.formatted_string(&feature_set))
 }
 
-fn process_activate(
+async fn process_activate(
     rpc_client: &RpcClient,
-    config: &CliConfig,
+    config: &CliConfig<'_>,
     feature_id: Pubkey,
     cluster: ClusterType,
     force: ForceActivation,
     fee_payer: SignerIndex,
 ) -> ProcessResult {
-    check_rpc_genesis_hash(&cluster, rpc_client)?;
+    check_rpc_genesis_hash(&cluster, rpc_client).await?;
 
     let fee_payer = config.signers[fee_payer];
     let account = rpc_client
-        .get_multiple_accounts(&[feature_id])?
+        .get_multiple_accounts(&[feature_id])
+        .await?
         .into_iter()
         .next()
         .unwrap();
@@ -1003,7 +1006,7 @@ fn process_activate(
         }
     }
 
-    if !feature_activation_allowed(rpc_client, false)?.0 {
+    if !feature_activation_allowed(rpc_client, false).await?.0 {
         match force {
             ForceActivation::Almost => {
                 return Err(
@@ -1019,9 +1022,11 @@ fn process_activate(
         }
     }
 
-    let rent = rpc_client.get_minimum_balance_for_rent_exemption(Feature::size_of())?;
+    let rent = rpc_client
+        .get_minimum_balance_for_rent_exemption(Feature::size_of())
+        .await?;
 
-    let blockhash = rpc_client.get_latest_blockhash()?;
+    let blockhash = rpc_client.get_latest_blockhash().await?;
     let (message, _) = resolve_spend_tx_and_check_account_balance(
         rpc_client,
         false,
@@ -1036,7 +1041,8 @@ fn process_activate(
             )
         },
         config.commitment,
-    )?;
+    )
+    .await?;
     let mut transaction = Transaction::new_unsigned(message);
     transaction.try_sign(&config.signers, blockhash)?;
 
@@ -1045,25 +1051,27 @@ fn process_activate(
         FEATURE_NAMES.get(&feature_id).unwrap(),
         feature_id
     );
-    let result = rpc_client.send_and_confirm_transaction_with_spinner_and_config(
-        &transaction,
-        config.commitment,
-        config.send_transaction_config,
-    );
+    let result = rpc_client
+        .send_and_confirm_transaction_with_spinner_and_config(
+            &transaction,
+            config.commitment,
+            config.send_transaction_config,
+        )
+        .await;
     log_instruction_custom_error::<SystemError>(result, config)
 }
 
-fn process_revoke(
+async fn process_revoke(
     rpc_client: &RpcClient,
-    config: &CliConfig,
+    config: &CliConfig<'_>,
     feature_id: Pubkey,
     cluster: ClusterType,
     fee_payer: SignerIndex,
 ) -> ProcessResult {
-    check_rpc_genesis_hash(&cluster, rpc_client)?;
+    check_rpc_genesis_hash(&cluster, rpc_client).await?;
 
     let fee_payer = config.signers[fee_payer];
-    let account = rpc_client.get_account(&feature_id).ok();
+    let account = rpc_client.get_account(&feature_id).await.ok();
 
     match account.and_then(status_from_account) {
         Some(CliFeatureStatus::Pending) => (),
@@ -1075,7 +1083,7 @@ fn process_revoke(
         }
     }
 
-    let blockhash = rpc_client.get_latest_blockhash()?;
+    let blockhash = rpc_client.get_latest_blockhash().await?;
     let (message, _) = resolve_spend_tx_and_check_account_balance(
         rpc_client,
         false,
@@ -1090,7 +1098,8 @@ fn process_revoke(
             )
         },
         config.commitment,
-    )?;
+    )
+    .await?;
     let mut transaction = Transaction::new_unsigned(message);
     transaction.try_sign(&config.signers, blockhash)?;
 
@@ -1099,10 +1108,12 @@ fn process_revoke(
         FEATURE_NAMES.get(&feature_id).unwrap(),
         feature_id
     );
-    let result = rpc_client.send_and_confirm_transaction_with_spinner_and_config(
-        &transaction,
-        config.commitment,
-        config.send_transaction_config,
-    );
+    let result = rpc_client
+        .send_and_confirm_transaction_with_spinner_and_config(
+            &transaction,
+            config.commitment,
+            config.send_transaction_config,
+        )
+        .await;
     log_instruction_custom_error_to_str::<FeatureGateError>(result, config)
 }

+ 15 - 13
cli/src/inflation.rs

@@ -12,7 +12,7 @@ use {
     solana_clock::{Epoch, Slot, UnixTimestamp},
     solana_pubkey::Pubkey,
     solana_remote_wallet::remote_wallet::RemoteWalletManager,
-    solana_rpc_client::rpc_client::RpcClient,
+    solana_rpc_client::nonblocking::rpc_client::RpcClient,
     std::{collections::HashMap, rc::Rc},
 };
 
@@ -72,22 +72,22 @@ pub fn parse_inflation_subcommand(
     )))
 }
 
-pub fn process_inflation_subcommand(
+pub async fn process_inflation_subcommand(
     rpc_client: &RpcClient,
-    config: &CliConfig,
+    config: &CliConfig<'_>,
     inflation_subcommand: &InflationCliCommand,
 ) -> ProcessResult {
     match inflation_subcommand {
-        InflationCliCommand::Show => process_show(rpc_client, config),
+        InflationCliCommand::Show => process_show(rpc_client, config).await,
         InflationCliCommand::Rewards(addresses, rewards_epoch) => {
-            process_rewards(rpc_client, config, addresses, *rewards_epoch)
+            process_rewards(rpc_client, config, addresses, *rewards_epoch).await
         }
     }
 }
 
-fn process_show(rpc_client: &RpcClient, config: &CliConfig) -> ProcessResult {
-    let governor = rpc_client.get_inflation_governor()?;
-    let current_rate = rpc_client.get_inflation_rate()?;
+async fn process_show(rpc_client: &RpcClient, config: &CliConfig<'_>) -> ProcessResult {
+    let governor = rpc_client.get_inflation_governor().await?;
+    let current_rate = rpc_client.get_inflation_rate().await?;
 
     let inflation = CliInflation {
         governor,
@@ -97,14 +97,15 @@ fn process_show(rpc_client: &RpcClient, config: &CliConfig) -> ProcessResult {
     Ok(config.output_format.formatted_string(&inflation))
 }
 
-fn process_rewards(
+async fn process_rewards(
     rpc_client: &RpcClient,
-    config: &CliConfig,
+    config: &CliConfig<'_>,
     addresses: &[Pubkey],
     rewards_epoch: Option<Epoch>,
 ) -> ProcessResult {
     let rewards = rpc_client
         .get_inflation_reward(addresses, rewards_epoch)
+        .await
         .map_err(|err| {
             if let Some(epoch) = rewards_epoch {
                 format!("Rewards not available for epoch {epoch}")
@@ -112,19 +113,20 @@ fn process_rewards(
                 format!("Rewards not available {err}")
             }
         })?;
-    let epoch_schedule = rpc_client.get_epoch_schedule()?;
+    let epoch_schedule = rpc_client.get_epoch_schedule().await?;
 
     let mut epoch_rewards: Vec<CliKeyedEpochReward> = vec![];
     let mut block_times: HashMap<Slot, UnixTimestamp> = HashMap::new();
     let epoch_metadata = if let Some(Some(first_reward)) = rewards.iter().find(|&v| v.is_some()) {
         let (epoch_start_time, epoch_end_time) =
-            crate::stake::get_epoch_boundary_timestamps(rpc_client, first_reward, &epoch_schedule)?;
+            crate::stake::get_epoch_boundary_timestamps(rpc_client, first_reward, &epoch_schedule)
+                .await?;
         for (reward, address) in rewards.iter().zip(addresses) {
             let cli_reward = if let Some(reward) = reward {
                 let block_time = if let Some(block_time) = block_times.get(&reward.effective_slot) {
                     *block_time
                 } else {
-                    let block_time = rpc_client.get_block_time(reward.effective_slot)?;
+                    let block_time = rpc_client.get_block_time(reward.effective_slot).await?;
                     block_times.insert(reward.effective_slot, block_time);
                     block_time
                 };

+ 7 - 4
cli/src/main.rs

@@ -229,7 +229,8 @@ pub fn parse_args<'a>(
     ))
 }
 
-fn main() -> Result<(), Box<dyn error::Error>> {
+#[tokio::main]
+async fn main() -> Result<(), Box<dyn error::Error>> {
     agave_logger::setup_with_default("off");
     let matches = get_clap_app(
         crate_name!(),
@@ -238,16 +239,18 @@ fn main() -> Result<(), Box<dyn error::Error>> {
     )
     .get_matches();
 
-    do_main(&matches).map_err(|err| DisplayError::new_as_boxed(err).into())
+    do_main(&matches)
+        .await
+        .map_err(|err| DisplayError::new_as_boxed(err).into())
 }
 
-fn do_main(matches: &ArgMatches<'_>) -> Result<(), Box<dyn error::Error>> {
+async fn do_main(matches: &ArgMatches<'_>) -> Result<(), Box<dyn error::Error>> {
     if parse_settings(matches)? {
         let mut wallet_manager = None;
 
         let (mut config, signers) = parse_args(matches, &mut wallet_manager)?;
         config.signers = signers.iter().map(|s| s.as_ref()).collect();
-        let result = process_command(&config)?;
+        let result = process_command(&config).await?;
         println!("{result}");
     };
     Ok(())

+ 97 - 77
cli/src/nonce.rs

@@ -24,11 +24,11 @@ use {
     solana_cli_output::CliNonceAccount,
     solana_hash::Hash,
     solana_message::Message,
-    solana_nonce::{self as nonce, state::State},
+    solana_nonce::state::State,
     solana_pubkey::Pubkey,
     solana_remote_wallet::remote_wallet::RemoteWalletManager,
-    solana_rpc_client::rpc_client::RpcClient,
-    solana_rpc_client_nonce_utils::*,
+    solana_rpc_client::nonblocking::rpc_client::RpcClient,
+    solana_rpc_client_nonce_utils::nonblocking::*,
     solana_sdk_ids::system_program,
     solana_system_interface::{
         error::SystemError,
@@ -403,16 +403,16 @@ pub fn check_nonce_account(
     }
 }
 
-pub fn process_authorize_nonce_account(
+pub async fn process_authorize_nonce_account(
     rpc_client: &RpcClient,
-    config: &CliConfig,
+    config: &CliConfig<'_>,
     nonce_account: &Pubkey,
     nonce_authority: SignerIndex,
     memo: Option<&String>,
     new_authority: &Pubkey,
     compute_unit_price: Option<u64>,
 ) -> ProcessResult {
-    let latest_blockhash = rpc_client.get_latest_blockhash()?;
+    let latest_blockhash = rpc_client.get_latest_blockhash().await?;
 
     let nonce_authority = config.signers[nonce_authority];
     let compute_unit_limit = ComputeUnitLimit::Simulated;
@@ -427,7 +427,7 @@ pub fn process_authorize_nonce_account(
         compute_unit_limit,
     });
     let mut message = Message::new(&ixs, Some(&config.signers[0].pubkey()));
-    simulate_and_update_compute_unit_limit(&compute_unit_limit, rpc_client, &mut message)?;
+    simulate_and_update_compute_unit_limit(&compute_unit_limit, rpc_client, &mut message).await?;
     let mut tx = Transaction::new_unsigned(message);
     tx.try_sign(&config.signers, latest_blockhash)?;
 
@@ -436,19 +436,22 @@ pub fn process_authorize_nonce_account(
         &config.signers[0].pubkey(),
         &tx.message,
         config.commitment,
-    )?;
-    let result = rpc_client.send_and_confirm_transaction_with_spinner_and_config(
-        &tx,
-        config.commitment,
-        config.send_transaction_config,
-    );
+    )
+    .await?;
+    let result = rpc_client
+        .send_and_confirm_transaction_with_spinner_and_config(
+            &tx,
+            config.commitment,
+            config.send_transaction_config,
+        )
+        .await;
 
     log_instruction_custom_error::<SystemError>(result, config)
 }
 
-pub fn process_create_nonce_account(
+pub async fn process_create_nonce_account(
     rpc_client: &RpcClient,
-    config: &CliConfig,
+    config: &CliConfig<'_>,
     nonce_account: SignerIndex,
     seed: Option<String>,
     nonce_authority: Option<Pubkey>,
@@ -468,7 +471,9 @@ pub fn process_create_nonce_account(
         (&nonce_account_address, "nonce_account".to_string()),
     )?;
 
-    let minimum_balance = rpc_client.get_minimum_balance_for_rent_exemption(State::size())?;
+    let minimum_balance = rpc_client
+        .get_minimum_balance_for_rent_exemption(State::size())
+        .await?;
     if amount == SpendAmount::All {
         amount = SpendAmount::AllForAccountCreation {
             create_account_min_balance: minimum_balance,
@@ -509,7 +514,7 @@ pub fn process_create_nonce_account(
         Message::new(&ixs, Some(&config.signers[0].pubkey()))
     };
 
-    let latest_blockhash = rpc_client.get_latest_blockhash()?;
+    let latest_blockhash = rpc_client.get_latest_blockhash().await?;
 
     let (message, lamports) = resolve_spend_tx_and_check_account_balance(
         rpc_client,
@@ -520,9 +525,10 @@ pub fn process_create_nonce_account(
         compute_unit_limit,
         build_message,
         config.commitment,
-    )?;
+    )
+    .await?;
 
-    if let Ok(nonce_account) = get_account(rpc_client, &nonce_account_address) {
+    if let Ok(nonce_account) = get_account(rpc_client, &nonce_account_address).await {
         let err_msg = if state_from_account(&nonce_account).is_ok() {
             format!("Nonce account {nonce_account_address} already exists")
         } else {
@@ -541,21 +547,24 @@ pub fn process_create_nonce_account(
 
     let mut tx = Transaction::new_unsigned(message);
     tx.try_sign(&config.signers, latest_blockhash)?;
-    let result = rpc_client.send_and_confirm_transaction_with_spinner_and_config(
-        &tx,
-        config.commitment,
-        config.send_transaction_config,
-    );
+    let result = rpc_client
+        .send_and_confirm_transaction_with_spinner_and_config(
+            &tx,
+            config.commitment,
+            config.send_transaction_config,
+        )
+        .await;
 
     log_instruction_custom_error::<SystemError>(result, config)
 }
 
-pub fn process_get_nonce(
+pub async fn process_get_nonce(
     rpc_client: &RpcClient,
-    config: &CliConfig,
+    config: &CliConfig<'_>,
     nonce_account_pubkey: &Pubkey,
 ) -> ProcessResult {
     match get_account_with_commitment(rpc_client, nonce_account_pubkey, config.commitment)
+        .await
         .and_then(|ref a| state_from_account(a))?
     {
         State::Uninitialized => Ok("Nonce account is uninitialized".to_string()),
@@ -563,9 +572,9 @@ pub fn process_get_nonce(
     }
 }
 
-pub fn process_new_nonce(
+pub async fn process_new_nonce(
     rpc_client: &RpcClient,
-    config: &CliConfig,
+    config: &CliConfig<'_>,
     nonce_account: &Pubkey,
     nonce_authority: SignerIndex,
     memo: Option<&String>,
@@ -576,7 +585,7 @@ pub fn process_new_nonce(
         (nonce_account, "nonce_account_pubkey".to_string()),
     )?;
 
-    if let Err(err) = rpc_client.get_account(nonce_account) {
+    if let Err(err) = rpc_client.get_account(nonce_account).await {
         return Err(CliError::BadParameter(format!(
             "Unable to advance nonce account {nonce_account}. error: {err}"
         ))
@@ -594,9 +603,9 @@ pub fn process_new_nonce(
         compute_unit_price,
         compute_unit_limit,
     });
-    let latest_blockhash = rpc_client.get_latest_blockhash()?;
+    let latest_blockhash = rpc_client.get_latest_blockhash().await?;
     let mut message = Message::new(&ixs, Some(&config.signers[0].pubkey()));
-    simulate_and_update_compute_unit_limit(&compute_unit_limit, rpc_client, &mut message)?;
+    simulate_and_update_compute_unit_limit(&compute_unit_limit, rpc_client, &mut message).await?;
     let mut tx = Transaction::new_unsigned(message);
     tx.try_sign(&config.signers, latest_blockhash)?;
     check_account_for_fee_with_commitment(
@@ -604,49 +613,54 @@ pub fn process_new_nonce(
         &config.signers[0].pubkey(),
         &tx.message,
         config.commitment,
-    )?;
-    let result = rpc_client.send_and_confirm_transaction_with_spinner_and_config(
-        &tx,
-        config.commitment,
-        config.send_transaction_config,
-    );
+    )
+    .await?;
+    let result = rpc_client
+        .send_and_confirm_transaction_with_spinner_and_config(
+            &tx,
+            config.commitment,
+            config.send_transaction_config,
+        )
+        .await;
 
     log_instruction_custom_error::<SystemError>(result, config)
 }
 
-pub fn process_show_nonce_account(
+pub async fn process_show_nonce_account(
     rpc_client: &RpcClient,
-    config: &CliConfig,
+    config: &CliConfig<'_>,
     nonce_account_pubkey: &Pubkey,
     use_lamports_unit: bool,
 ) -> ProcessResult {
     let nonce_account =
-        get_account_with_commitment(rpc_client, nonce_account_pubkey, config.commitment)?;
-    let print_account = |data: Option<&nonce::state::Data>| {
-        let mut nonce_account = CliNonceAccount {
-            balance: nonce_account.lamports,
-            minimum_balance_for_rent_exemption: rpc_client
-                .get_minimum_balance_for_rent_exemption(State::size())?,
-            use_lamports_unit,
-            ..CliNonceAccount::default()
-        };
-        if let Some(data) = data {
-            nonce_account.nonce = Some(data.blockhash().to_string());
-            nonce_account.lamports_per_signature = Some(data.fee_calculator.lamports_per_signature);
-            nonce_account.authority = Some(data.authority.to_string());
-        }
-
-        Ok(config.output_format.formatted_string(&nonce_account))
+        get_account_with_commitment(rpc_client, nonce_account_pubkey, config.commitment).await?;
+    let minimum_balance_for_rent_exemption = rpc_client
+        .get_minimum_balance_for_rent_exemption(State::size())
+        .await?;
+
+    let mut cli_nonce_account = CliNonceAccount {
+        balance: nonce_account.lamports,
+        minimum_balance_for_rent_exemption,
+        use_lamports_unit,
+        ..CliNonceAccount::default()
     };
+
     match state_from_account(&nonce_account)? {
-        State::Uninitialized => print_account(None),
-        State::Initialized(ref data) => print_account(Some(data)),
+        State::Uninitialized => {}
+        State::Initialized(ref data) => {
+            cli_nonce_account.nonce = Some(data.blockhash().to_string());
+            cli_nonce_account.lamports_per_signature =
+                Some(data.fee_calculator.lamports_per_signature);
+            cli_nonce_account.authority = Some(data.authority.to_string());
+        }
     }
+
+    Ok(config.output_format.formatted_string(&cli_nonce_account))
 }
 
-pub fn process_withdraw_from_nonce_account(
+pub async fn process_withdraw_from_nonce_account(
     rpc_client: &RpcClient,
-    config: &CliConfig,
+    config: &CliConfig<'_>,
     nonce_account: &Pubkey,
     nonce_authority: SignerIndex,
     memo: Option<&String>,
@@ -654,7 +668,7 @@ pub fn process_withdraw_from_nonce_account(
     lamports: u64,
     compute_unit_price: Option<u64>,
 ) -> ProcessResult {
-    let latest_blockhash = rpc_client.get_latest_blockhash()?;
+    let latest_blockhash = rpc_client.get_latest_blockhash().await?;
 
     let nonce_authority = config.signers[nonce_authority];
     let compute_unit_limit = ComputeUnitLimit::Simulated;
@@ -670,7 +684,7 @@ pub fn process_withdraw_from_nonce_account(
         compute_unit_limit,
     });
     let mut message = Message::new(&ixs, Some(&config.signers[0].pubkey()));
-    simulate_and_update_compute_unit_limit(&compute_unit_limit, rpc_client, &mut message)?;
+    simulate_and_update_compute_unit_limit(&compute_unit_limit, rpc_client, &mut message).await?;
     let mut tx = Transaction::new_unsigned(message);
     tx.try_sign(&config.signers, latest_blockhash)?;
     check_account_for_fee_with_commitment(
@@ -678,24 +692,27 @@ pub fn process_withdraw_from_nonce_account(
         &config.signers[0].pubkey(),
         &tx.message,
         config.commitment,
-    )?;
-    let result = rpc_client.send_and_confirm_transaction_with_spinner_and_config(
-        &tx,
-        config.commitment,
-        config.send_transaction_config,
-    );
+    )
+    .await?;
+    let result = rpc_client
+        .send_and_confirm_transaction_with_spinner_and_config(
+            &tx,
+            config.commitment,
+            config.send_transaction_config,
+        )
+        .await;
 
     log_instruction_custom_error::<SystemError>(result, config)
 }
 
-pub(crate) fn process_upgrade_nonce_account(
+pub(crate) async fn process_upgrade_nonce_account(
     rpc_client: &RpcClient,
-    config: &CliConfig,
+    config: &CliConfig<'_>,
     nonce_account: Pubkey,
     memo: Option<&String>,
     compute_unit_price: Option<u64>,
 ) -> ProcessResult {
-    let latest_blockhash = rpc_client.get_latest_blockhash()?;
+    let latest_blockhash = rpc_client.get_latest_blockhash().await?;
     let compute_unit_limit = ComputeUnitLimit::Simulated;
     let ixs = vec![upgrade_nonce_account(nonce_account)]
         .with_memo(memo)
@@ -704,7 +721,7 @@ pub(crate) fn process_upgrade_nonce_account(
             compute_unit_limit,
         });
     let mut message = Message::new(&ixs, Some(&config.signers[0].pubkey()));
-    simulate_and_update_compute_unit_limit(&compute_unit_limit, rpc_client, &mut message)?;
+    simulate_and_update_compute_unit_limit(&compute_unit_limit, rpc_client, &mut message).await?;
     let mut tx = Transaction::new_unsigned(message);
     tx.try_sign(&config.signers, latest_blockhash)?;
     check_account_for_fee_with_commitment(
@@ -712,12 +729,15 @@ pub(crate) fn process_upgrade_nonce_account(
         &config.signers[0].pubkey(),
         &tx.message,
         config.commitment,
-    )?;
-    let result = rpc_client.send_and_confirm_transaction_with_spinner_and_config(
-        &tx,
-        config.commitment,
-        config.send_transaction_config,
-    );
+    )
+    .await?;
+    let result = rpc_client
+        .send_and_confirm_transaction_with_spinner_and_config(
+            &tx,
+            config.commitment,
+            config.send_transaction_config,
+        )
+        .await;
     log_instruction_custom_error::<SystemError>(result, config)
 }
 

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 285 - 209
cli/src/program.rs


+ 282 - 133
cli/src/program_v4.rs

@@ -26,9 +26,8 @@ use {
     solana_client::{
         connection_cache::ConnectionCache,
         send_and_confirm_transactions_in_parallel::{
-            send_and_confirm_transactions_in_parallel_blocking_v2, SendAndConfirmConfigV2,
+            send_and_confirm_transactions_in_parallel_v2, SendAndConfirmConfigV2,
         },
-        tpu_client::{TpuClient, TpuClientConfig},
     },
     solana_instruction::Instruction,
     solana_loader_v4_interface::{
@@ -44,17 +43,18 @@ use {
     },
     solana_pubkey::Pubkey,
     solana_remote_wallet::remote_wallet::RemoteWalletManager,
-    solana_rpc_client::rpc_client::RpcClient,
+    solana_rpc_client::nonblocking::rpc_client::RpcClient,
     solana_rpc_client_api::{
         config::{RpcAccountInfoConfig, RpcProgramAccountsConfig},
         filter::{Memcmp, RpcFilterType},
         request::MAX_MULTIPLE_ACCOUNTS,
     },
-    solana_rpc_client_nonce_utils::blockhash_query::BlockhashQuery,
+    solana_rpc_client_nonce_utils::nonblocking::blockhash_query::BlockhashQuery,
     solana_sbpf::{elf::Executable, verifier::RequisiteVerifier},
     solana_sdk_ids::{loader_v4, system_program},
     solana_signer::Signer,
     solana_system_interface::{instruction as system_instruction, MAX_PERMITTED_DATA_LENGTH},
+    solana_tpu_client::tpu_client::TpuClientConfig,
     solana_transaction::Transaction,
     std::{
         cmp::Ordering,
@@ -530,9 +530,9 @@ pub fn parse_program_v4_subcommand(
     Ok(response)
 }
 
-pub fn process_program_v4_subcommand(
+pub async fn process_program_v4_subcommand(
     rpc_client: Arc<RpcClient>,
-    config: &CliConfig,
+    config: &CliConfig<'_>,
     program_subcommand: &ProgramV4CliCommand,
 ) -> ProcessResult {
     match program_subcommand {
@@ -563,55 +563,65 @@ pub fn process_program_v4_subcommand(
                 &program_data,
                 upload_range.clone(),
             )
+            .await
         }
         ProgramV4CliCommand::Retract {
             additional_cli_config,
             program_address,
             authority_signer_index,
             close_program_entirely,
-        } => process_retract_program(
-            rpc_client,
-            config,
-            additional_cli_config,
-            authority_signer_index,
-            program_address,
-            *close_program_entirely,
-        ),
+        } => {
+            process_retract_program(
+                rpc_client,
+                config,
+                additional_cli_config,
+                authority_signer_index,
+                program_address,
+                *close_program_entirely,
+            )
+            .await
+        }
         ProgramV4CliCommand::TransferAuthority {
             additional_cli_config,
             program_address,
             authority_signer_index,
             new_authority_signer_index,
-        } => process_transfer_authority_of_program(
-            rpc_client,
-            config,
-            additional_cli_config,
-            authority_signer_index,
-            new_authority_signer_index,
-            program_address,
-        ),
+        } => {
+            process_transfer_authority_of_program(
+                rpc_client,
+                config,
+                additional_cli_config,
+                authority_signer_index,
+                new_authority_signer_index,
+                program_address,
+            )
+            .await
+        }
         ProgramV4CliCommand::Finalize {
             additional_cli_config,
             program_address,
             authority_signer_index,
             next_version_signer_index,
-        } => process_finalize_program(
-            rpc_client,
-            config,
-            additional_cli_config,
-            authority_signer_index,
-            next_version_signer_index,
-            program_address,
-        ),
+        } => {
+            process_finalize_program(
+                rpc_client,
+                config,
+                additional_cli_config,
+                authority_signer_index,
+                next_version_signer_index,
+                program_address,
+            )
+            .await
+        }
         ProgramV4CliCommand::Show {
             account_pubkey,
             authority,
             all,
-        } => process_show(rpc_client, config, *account_pubkey, *authority, *all),
+        } => process_show(rpc_client, config, *account_pubkey, *authority, *all).await,
         ProgramV4CliCommand::Dump {
             account_pubkey,
             output_location,
-        } => process_dump(rpc_client, config, *account_pubkey, output_location),
+        } => process_dump(rpc_client, config, *account_pubkey, output_location).await,
     }
 }
 
@@ -631,9 +641,9 @@ pub fn process_program_v4_subcommand(
 // * Two-step redeploy an existing program using a buffer account
 //   - buffer_address must be `Some(buffer_signer.pubkey())`
 //   - upload_signer_index must be None
-pub fn process_deploy_program(
+pub async fn process_deploy_program(
     rpc_client: Arc<RpcClient>,
-    config: &CliConfig,
+    config: &CliConfig<'_>,
     additional_cli_config: &AdditionalCliConfig,
     program_address: &Pubkey,
     buffer_address: Option<&Pubkey>,
@@ -647,18 +657,22 @@ pub fn process_deploy_program(
 
     // Check requested command makes sense given the on-chain state
     let program_account = rpc_client
-        .get_account_with_commitment(program_address, config.commitment)?
+        .get_account_with_commitment(program_address, config.commitment)
+        .await?
         .value;
     let buffer_account = if let Some(buffer_address) = buffer_address {
         rpc_client
-            .get_account_with_commitment(buffer_address, config.commitment)?
+            .get_account_with_commitment(buffer_address, config.commitment)
+            .await?
             .value
     } else {
         None
     };
-    let lamports_required = rpc_client.get_minimum_balance_for_rent_exemption(
-        LoaderV4State::program_data_offset().saturating_add(program_data.len()),
-    )?;
+    let lamports_required = rpc_client
+        .get_minimum_balance_for_rent_exemption(
+            LoaderV4State::program_data_offset().saturating_add(program_data.len()),
+        )
+        .await?;
     let program_account_exists = program_account
         .as_ref()
         .map(|account| loader_v4::check_id(&account.owner))
@@ -709,7 +723,8 @@ pub fn process_deploy_program(
         .chunks(MAX_MULTIPLE_ACCOUNTS)
     {
         rpc_client
-            .get_multiple_accounts(feature_ids)?
+            .get_multiple_accounts(feature_ids)
+            .await?
             .into_iter()
             .zip(feature_ids)
             .for_each(|(account, feature_id)| {
@@ -774,7 +789,8 @@ pub fn process_deploy_program(
                 program_account,
                 program_address,
                 program_data.len() as u32,
-            )?;
+            )
+            .await?;
         if !set_program_length_instructions.is_empty() {
             initial_instructions.append(&mut set_program_length_instructions);
         }
@@ -895,11 +911,12 @@ pub fn process_deploy_program(
             signature: None,
         }),
     )
+    .await
 }
 
-fn process_retract_program(
+async fn process_retract_program(
     rpc_client: Arc<RpcClient>,
-    config: &CliConfig,
+    config: &CliConfig<'_>,
     additional_cli_config: &AdditionalCliConfig,
     auth_signer_index: &SignerIndex,
     program_address: &Pubkey,
@@ -909,7 +926,8 @@ fn process_retract_program(
     let authority_pubkey = config.signers[*auth_signer_index].pubkey();
 
     let Some(program_account) = rpc_client
-        .get_account_with_commitment(program_address, config.commitment)?
+        .get_account_with_commitment(program_address, config.commitment)
+        .await?
         .value
     else {
         return Err("Program account does not exist".into());
@@ -946,18 +964,20 @@ fn process_retract_program(
             signature: None,
         }),
     )
+    .await
 }
 
-fn process_transfer_authority_of_program(
+async fn process_transfer_authority_of_program(
     rpc_client: Arc<RpcClient>,
-    config: &CliConfig,
+    config: &CliConfig<'_>,
     additional_cli_config: &AdditionalCliConfig,
     auth_signer_index: &SignerIndex,
     new_auth_signer_index: &SignerIndex,
     program_address: &Pubkey,
 ) -> ProcessResult {
     if let Some(program_account) = rpc_client
-        .get_account_with_commitment(program_address, config.commitment)?
+        .get_account_with_commitment(program_address, config.commitment)
+        .await?
         .value
     {
         if !loader_v4::check_id(&program_account.owner) {
@@ -990,18 +1010,20 @@ fn process_transfer_authority_of_program(
             signature: None,
         }),
     )
+    .await
 }
 
-fn process_finalize_program(
+async fn process_finalize_program(
     rpc_client: Arc<RpcClient>,
-    config: &CliConfig,
+    config: &CliConfig<'_>,
     additional_cli_config: &AdditionalCliConfig,
     auth_signer_index: &SignerIndex,
     next_version_signer_index: &SignerIndex,
     program_address: &Pubkey,
 ) -> ProcessResult {
     if let Some(program_account) = rpc_client
-        .get_account_with_commitment(program_address, config.commitment)?
+        .get_account_with_commitment(program_address, config.commitment)
+        .await?
         .value
     {
         if !loader_v4::check_id(&program_account.owner) {
@@ -1034,18 +1056,20 @@ fn process_finalize_program(
             signature: None,
         }),
     )
+    .await
 }
 
-fn process_show(
+async fn process_show(
     rpc_client: Arc<RpcClient>,
-    config: &CliConfig,
+    config: &CliConfig<'_>,
     program_address: Option<Pubkey>,
     authority: Pubkey,
     all: bool,
 ) -> ProcessResult {
     if let Some(program_address) = program_address {
         if let Some(account) = rpc_client
-            .get_account_with_commitment(&program_address, config.commitment)?
+            .get_account_with_commitment(&program_address, config.commitment)
+            .await?
             .value
         {
             if loader_v4::check_id(&account.owner) {
@@ -1077,20 +1101,21 @@ fn process_show(
         }
     } else {
         let authority_pubkey = if all { None } else { Some(authority) };
-        let programs = get_programs(rpc_client, config, authority_pubkey)?;
+        let programs = get_programs(rpc_client, config, authority_pubkey).await?;
         Ok(config.output_format.formatted_string(&programs))
     }
 }
 
-pub fn process_dump(
+pub async fn process_dump(
     rpc_client: Arc<RpcClient>,
-    config: &CliConfig,
+    config: &CliConfig<'_>,
     account_pubkey: Option<Pubkey>,
     output_location: &str,
 ) -> ProcessResult {
     if let Some(account_pubkey) = account_pubkey {
         if let Some(account) = rpc_client
-            .get_account_with_commitment(&account_pubkey, config.commitment)?
+            .get_account_with_commitment(&account_pubkey, config.commitment)
+            .await?
             .value
         {
             if loader_v4::check_id(&account.owner) {
@@ -1108,10 +1133,94 @@ pub fn process_dump(
     }
 }
 
+/// Synchronous wrapper for process_deploy_program
+/// Use this when calling from a blocking context within an async runtime
+/// Accepts a blocking RpcClient and converts it internally to nonblocking
+///
+/// # Performance Note
+/// This function creates a new RpcClient on each call and has overhead from
+/// sync-async-sync bridging. For better performance, consider using the async
+/// `process_deploy_program()` directly with a nonblocking RpcClient.
+#[deprecated(
+    note = "Consider using async process_deploy_program() with nonblocking RpcClient for better \
+            performance and resource usage"
+)]
+pub fn process_deploy_program_sync(
+    rpc_client_blocking: Arc<solana_rpc_client::rpc_client::RpcClient>,
+    config: &CliConfig<'_>,
+    additional_cli_config: &AdditionalCliConfig,
+    program_address: &Pubkey,
+    buffer_address: Option<&Pubkey>,
+    upload_signer_index: Option<&SignerIndex>,
+    auth_signer_index: &SignerIndex,
+    program_data: &[u8],
+    upload_range: Range<Option<usize>>,
+) -> ProcessResult {
+    tokio::task::block_in_place(|| {
+        tokio::runtime::Handle::current().block_on(async {
+            // Convert blocking RpcClient to nonblocking
+            let rpc_client_nonblocking = Arc::new(RpcClient::new_with_commitment(
+                rpc_client_blocking.url(),
+                rpc_client_blocking.commitment(),
+            ));
+
+            process_deploy_program(
+                rpc_client_nonblocking,
+                config,
+                additional_cli_config,
+                program_address,
+                buffer_address,
+                upload_signer_index,
+                auth_signer_index,
+                program_data,
+                upload_range,
+            )
+            .await
+        })
+    })
+}
+
+/// Synchronous wrapper for process_dump
+/// Use this when calling from a blocking context within an async runtime
+/// Accepts a blocking RpcClient and converts it internally to nonblocking
+///
+/// # Performance Note
+/// This function creates a new RpcClient on each call and has overhead from
+/// sync-async-sync bridging. For better performance, consider using the async
+/// `process_dump()` directly with a nonblocking RpcClient.
+#[deprecated(
+    note = "Consider using async process_dump() with nonblocking RpcClient for better performance \
+            and resource usage"
+)]
+pub fn process_dump_sync(
+    rpc_client_blocking: Arc<solana_rpc_client::rpc_client::RpcClient>,
+    config: &CliConfig<'_>,
+    account_pubkey: Option<Pubkey>,
+    output_location: &str,
+) -> ProcessResult {
+    tokio::task::block_in_place(|| {
+        tokio::runtime::Handle::current().block_on(async {
+            // Convert blocking RpcClient to nonblocking
+            let rpc_client_nonblocking = Arc::new(RpcClient::new_with_commitment(
+                rpc_client_blocking.url(),
+                rpc_client_blocking.commitment(),
+            ));
+
+            process_dump(
+                rpc_client_nonblocking,
+                config,
+                account_pubkey,
+                output_location,
+            )
+            .await
+        })
+    })
+}
+
 #[allow(clippy::too_many_arguments)]
-fn send_messages(
+async fn send_messages(
     rpc_client: Arc<RpcClient>,
-    config: &CliConfig,
+    config: &CliConfig<'_>,
     additional_cli_config: &AdditionalCliConfig,
     auth_signer_index: &SignerIndex,
     initial_messages: Vec<Vec<Instruction>>,
@@ -1123,12 +1232,13 @@ fn send_messages(
     let payer_pubkey = config.signers[0].pubkey();
     let blockhash = additional_cli_config
         .blockhash_query
-        .get_blockhash(&rpc_client, config.commitment)?;
+        .get_blockhash(&rpc_client, config.commitment)
+        .await?;
     let compute_unit_config = ComputeUnitConfig {
         compute_unit_price: additional_cli_config.compute_unit_price,
         compute_unit_limit: ComputeUnitLimit::Simulated,
     };
-    let simulate_messages = |message_prototypes: Vec<Vec<Instruction>>| {
+    let simulate_messages = |message_prototypes: Vec<Vec<Instruction>>| async {
         let mut messages = Vec::with_capacity(message_prototypes.len());
         for instructions in message_prototypes.into_iter() {
             let mut message = Message::new_with_blockhash(
@@ -1140,26 +1250,28 @@ fn send_messages(
                 &ComputeUnitLimit::Simulated,
                 &rpc_client,
                 &mut message,
-            )?;
+            )
+            .await?;
             messages.push(message);
         }
         Ok::<Vec<solana_message::Message>, Box<dyn std::error::Error>>(messages)
     };
-    let initial_messages = simulate_messages(initial_messages)?;
-    let write_messages = simulate_messages(write_messages)?;
-    let final_messages = simulate_messages(final_messages)?;
+    let initial_messages = simulate_messages(initial_messages).await?;
+    let write_messages = simulate_messages(write_messages).await?;
+    let final_messages = simulate_messages(final_messages).await?;
 
     let mut fee = Saturating(0);
     for message in initial_messages.iter() {
-        fee += rpc_client.get_fee_for_message(message)?;
+        fee += rpc_client.get_fee_for_message(message).await?;
     }
     for message in final_messages.iter() {
-        fee += rpc_client.get_fee_for_message(message)?;
+        fee += rpc_client.get_fee_for_message(message).await?;
     }
     // Assume all write messages cost the same
     if let Some(message) = write_messages.first() {
         fee += rpc_client
-            .get_fee_for_message(message)?
+            .get_fee_for_message(message)
+            .await?
             .saturating_mul(write_messages.len() as u64);
     }
     check_account_for_spend_and_fee_with_commitment(
@@ -1168,9 +1280,10 @@ fn send_messages(
         balance_needed,
         fee.0,
         config.commitment,
-    )?;
+    )
+    .await?;
 
-    let send_or_return_message = |message: Message| {
+    let send_or_return_message = |message: Message| async {
         let signers = (0..message.header.num_required_signatures)
             .map(|signer_index| {
                 let key = message.account_keys[signer_index as usize];
@@ -1198,13 +1311,14 @@ fn send_messages(
                     config.commitment,
                     config.send_transaction_config,
                 )
+                .await
                 .map_err(|err| format!("Failed to send message: {err}").into())
                 .map(|_| String::new())
         }
     };
 
     for message in initial_messages.into_iter() {
-        let result = send_or_return_message(message)?;
+        let result = send_or_return_message(message).await?;
         if additional_cli_config.sign_only {
             return Ok(result);
         }
@@ -1213,38 +1327,46 @@ fn send_messages(
     if !write_messages.is_empty() {
         let connection_cache = {
             #[cfg(feature = "dev-context-only-utils")]
-            let cache = ConnectionCache::new_quic_for_tests("connection_cache_cli_program_quic", 1);
+            let cache =
+                ConnectionCache::new_quic_for_tests("connection_cache_cli_program_v4_quic", 1);
             #[cfg(not(feature = "dev-context-only-utils"))]
-            let cache = ConnectionCache::new_quic("connection_cache_cli_program_quic", 1);
+            let cache = ConnectionCache::new_quic("connection_cache_cli_program_v4_quic", 1);
             cache
         };
+
         let transaction_errors = match connection_cache {
-            ConnectionCache::Udp(cache) => TpuClient::new_with_connection_cache(
-                rpc_client.clone(),
-                &config.websocket_url,
-                TpuClientConfig::default(),
-                cache,
-            )?
-            .send_and_confirm_messages_with_spinner(
-                &write_messages,
-                &[config.signers[0], config.signers[*auth_signer_index]],
-            ),
+            ConnectionCache::Udp(cache) => {
+                solana_tpu_client::nonblocking::tpu_client::TpuClient::new_with_connection_cache(
+                    rpc_client.clone(),
+                    &config.websocket_url,
+                    TpuClientConfig::default(),
+                    cache,
+                )
+                .await?
+                .send_and_confirm_messages_with_spinner(
+                    &write_messages,
+                    &[config.signers[0], config.signers[*auth_signer_index]],
+                )
+                .await
+            }
             ConnectionCache::Quic(cache) => {
-                let tpu_client_fut =
-                    solana_client::nonblocking::tpu_client::TpuClient::new_with_connection_cache(
-                        rpc_client.get_inner_client().clone(),
-                        &config.websocket_url,
-                        solana_client::tpu_client::TpuClientConfig::default(),
-                        cache,
-                    );
-                let tpu_client = (!additional_cli_config.use_rpc).then(|| {
-                    rpc_client
-                        .runtime()
-                        .block_on(tpu_client_fut)
-                        .expect("Should return a valid tpu client")
-                });
-
-                send_and_confirm_transactions_in_parallel_blocking_v2(
+                let tpu_client = if additional_cli_config.use_rpc {
+                    None
+                } else {
+                    // `solana_client` type currently required by `send_and_confirm_transactions_in_parallel_v2`
+                    Some(
+                        solana_client::nonblocking::tpu_client::TpuClient::new_with_connection_cache(
+                            rpc_client.clone(),
+                            &config.websocket_url,
+                            TpuClientConfig::default(),
+                            cache,
+                        )
+                        .await
+                        .expect("Should return a valid tpu client"),
+                    )
+                };
+
+                send_and_confirm_transactions_in_parallel_v2(
                     rpc_client.clone(),
                     tpu_client,
                     &write_messages,
@@ -1255,6 +1377,7 @@ fn send_messages(
                         rpc_send_transaction_config: config.send_transaction_config,
                     },
                 )
+                .await
             }
         }
         .map_err(|err| format!("Data writes to account failed: {err}"))?
@@ -1271,7 +1394,7 @@ fn send_messages(
     }
 
     for message in final_messages.into_iter() {
-        let result = send_or_return_message(message)?;
+        let result = send_or_return_message(message).await?;
         if additional_cli_config.sign_only {
             return Ok(result);
         }
@@ -1311,9 +1434,9 @@ fn build_retract_instruction(
     }
 }
 
-fn build_set_program_length_instructions(
+async fn build_set_program_length_instructions(
     rpc_client: Arc<RpcClient>,
-    config: &CliConfig,
+    config: &CliConfig<'_>,
     auth_signer_index: &SignerIndex,
     account: &Account,
     buffer_address: &Pubkey,
@@ -1322,8 +1445,9 @@ fn build_set_program_length_instructions(
     let expected_account_data_len =
         LoaderV4State::program_data_offset().saturating_add(program_data_length as usize);
 
-    let lamports_required =
-        rpc_client.get_minimum_balance_for_rent_exemption(expected_account_data_len)?;
+    let lamports_required = rpc_client
+        .get_minimum_balance_for_rent_exemption(expected_account_data_len)
+        .await?;
 
     if !loader_v4::check_id(&account.owner) {
         return Ok((Vec::default(), lamports_required));
@@ -1334,8 +1458,9 @@ fn build_set_program_length_instructions(
     let expected_account_data_len =
         LoaderV4State::program_data_offset().saturating_add(program_data_length as usize);
 
-    let lamports_required =
-        rpc_client.get_minimum_balance_for_rent_exemption(expected_account_data_len)?;
+    let lamports_required = rpc_client
+        .get_minimum_balance_for_rent_exemption(expected_account_data_len)
+        .await?;
 
     if !loader_v4::check_id(&account.owner) {
         return Ok((Vec::default(), lamports_required));
@@ -1403,30 +1528,32 @@ fn build_set_program_length_instructions(
     }
 }
 
-fn get_accounts_with_filter(
+async fn get_accounts_with_filter(
     rpc_client: Arc<RpcClient>,
-    _config: &CliConfig,
+    _config: &CliConfig<'_>,
     filters: Vec<RpcFilterType>,
     length: usize,
 ) -> Result<Vec<(Pubkey, UiAccount)>, Box<dyn std::error::Error>> {
-    let results = rpc_client.get_program_ui_accounts_with_config(
-        &loader_v4::id(),
-        RpcProgramAccountsConfig {
-            filters: Some(filters),
-            account_config: RpcAccountInfoConfig {
-                encoding: Some(UiAccountEncoding::Base64),
-                data_slice: Some(UiDataSliceConfig { offset: 0, length }),
-                ..RpcAccountInfoConfig::default()
+    let results = rpc_client
+        .get_program_ui_accounts_with_config(
+            &loader_v4::id(),
+            RpcProgramAccountsConfig {
+                filters: Some(filters),
+                account_config: RpcAccountInfoConfig {
+                    encoding: Some(UiAccountEncoding::Base64),
+                    data_slice: Some(UiDataSliceConfig { offset: 0, length }),
+                    ..RpcAccountInfoConfig::default()
+                },
+                ..RpcProgramAccountsConfig::default()
             },
-            ..RpcProgramAccountsConfig::default()
-        },
-    )?;
+        )
+        .await?;
     Ok(results)
 }
 
-fn get_programs(
+async fn get_programs(
     rpc_client: Arc<RpcClient>,
-    config: &CliConfig,
+    config: &CliConfig<'_>,
     authority_pubkey: Option<Pubkey>,
 ) -> Result<CliProgramsV4, Box<dyn std::error::Error>> {
     let filters = if let Some(authority_pubkey) = authority_pubkey {
@@ -1445,7 +1572,8 @@ fn get_programs(
         config,
         filters,
         LoaderV4State::program_data_offset(),
-    )?;
+    )
+    .await?;
 
     let mut programs = vec![];
     for (program, account) in results.iter() {
@@ -1557,8 +1685,8 @@ mod tests {
         )
     }
 
-    #[test]
-    fn test_deploy() {
+    #[tokio::test]
+    async fn test_deploy() {
         let mut config = CliConfig::default();
         let mut program_data = Vec::new();
         let mut file = File::open("tests/fixtures/noop.so").unwrap();
@@ -1583,6 +1711,7 @@ mod tests {
             &program_data,
             None..None,
         )
+        .await
         .is_ok());
 
         assert!(process_deploy_program(
@@ -1596,6 +1725,7 @@ mod tests {
             &program_data,
             None..None,
         )
+        .await
         .is_ok());
 
         assert!(process_deploy_program(
@@ -1609,6 +1739,7 @@ mod tests {
             &program_data,
             None..None,
         )
+        .await
         .is_err());
 
         assert!(process_deploy_program(
@@ -1622,11 +1753,12 @@ mod tests {
             &program_data,
             None..None,
         )
+        .await
         .is_err());
     }
 
-    #[test]
-    fn test_redeploy() {
+    #[tokio::test]
+    async fn test_redeploy() {
         let mut config = CliConfig::default();
         let mut program_data = Vec::new();
         let mut file = File::open("tests/fixtures/noop.so").unwrap();
@@ -1651,6 +1783,7 @@ mod tests {
             &program_data,
             None..None,
         )
+        .await
         .is_err());
 
         assert!(process_deploy_program(
@@ -1664,6 +1797,7 @@ mod tests {
             &program_data,
             None..None,
         )
+        .await
         .is_ok());
 
         assert!(process_deploy_program(
@@ -1677,6 +1811,7 @@ mod tests {
             &program_data,
             None..None,
         )
+        .await
         .is_ok());
 
         assert!(process_deploy_program(
@@ -1690,6 +1825,7 @@ mod tests {
             &program_data,
             None..None,
         )
+        .await
         .is_err());
 
         assert!(process_deploy_program(
@@ -1703,6 +1839,7 @@ mod tests {
             &program_data,
             None..None,
         )
+        .await
         .is_err());
 
         assert!(process_deploy_program(
@@ -1716,11 +1853,12 @@ mod tests {
             &program_data,
             None..None,
         )
+        .await
         .is_err());
     }
 
-    #[test]
-    fn test_redeploy_from_source() {
+    #[tokio::test]
+    async fn test_redeploy_from_source() {
         let mut config = CliConfig::default();
         let mut program_data = Vec::new();
         let mut file = File::open("tests/fixtures/noop.so").unwrap();
@@ -1747,6 +1885,7 @@ mod tests {
             &program_data,
             None..None,
         )
+        .await
         .is_err());
 
         assert!(process_deploy_program(
@@ -1760,6 +1899,7 @@ mod tests {
             &program_data,
             None..None,
         )
+        .await
         .is_err());
 
         assert!(process_deploy_program(
@@ -1773,11 +1913,12 @@ mod tests {
             &program_data,
             None..None,
         )
+        .await
         .is_err());
     }
 
-    #[test]
-    fn test_retract() {
+    #[tokio::test]
+    async fn test_retract() {
         let mut config = CliConfig::default();
 
         let payer = keypair_from_seed(&[1u8; 32]).unwrap();
@@ -1796,6 +1937,7 @@ mod tests {
                 &program_signer.pubkey(),
                 close_program_entirely,
             )
+            .await
             .is_err());
 
             assert!(
@@ -1807,6 +1949,7 @@ mod tests {
                     &program_signer.pubkey(),
                     close_program_entirely,
                 )
+                .await
                 .is_ok()
                     == close_program_entirely
             );
@@ -1819,6 +1962,7 @@ mod tests {
                 &program_signer.pubkey(),
                 close_program_entirely,
             )
+            .await
             .is_ok());
 
             assert!(process_retract_program(
@@ -1829,6 +1973,7 @@ mod tests {
                 &program_signer.pubkey(),
                 close_program_entirely,
             )
+            .await
             .is_err());
 
             assert!(process_retract_program(
@@ -1839,6 +1984,7 @@ mod tests {
                 &program_signer.pubkey(),
                 close_program_entirely,
             )
+            .await
             .is_err());
 
             assert!(process_retract_program(
@@ -1849,12 +1995,13 @@ mod tests {
                 &program_signer.pubkey(),
                 close_program_entirely,
             )
+            .await
             .is_err());
         }
     }
 
-    #[test]
-    fn test_transfer_authority() {
+    #[tokio::test]
+    async fn test_transfer_authority() {
         let mut config = CliConfig::default();
 
         let payer = keypair_from_seed(&[1u8; 32]).unwrap();
@@ -1874,11 +2021,12 @@ mod tests {
             &2,
             &program_signer.pubkey(),
         )
+        .await
         .is_ok());
     }
 
-    #[test]
-    fn test_finalize() {
+    #[tokio::test]
+    async fn test_finalize() {
         let mut config = CliConfig::default();
 
         let payer = keypair_from_seed(&[1u8; 32]).unwrap();
@@ -1898,6 +2046,7 @@ mod tests {
             &2,
             &program_signer.pubkey(),
         )
+        .await
         .is_ok());
     }
 

+ 21 - 12
cli/src/spend_utils.rs

@@ -14,7 +14,7 @@ use {
     solana_hash::Hash,
     solana_message::Message,
     solana_pubkey::Pubkey,
-    solana_rpc_client::rpc_client::RpcClient,
+    solana_rpc_client::nonblocking::rpc_client::RpcClient,
 };
 
 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
@@ -60,7 +60,7 @@ struct SpendAndFee {
     fee: u64,
 }
 
-pub fn resolve_spend_tx_and_check_account_balance<F>(
+pub async fn resolve_spend_tx_and_check_account_balance<F>(
     rpc_client: &RpcClient,
     sign_only: bool,
     amount: SpendAmount,
@@ -84,9 +84,10 @@ where
         build_message,
         commitment,
     )
+    .await
 }
 
-pub fn resolve_spend_tx_and_check_account_balances<F>(
+pub async fn resolve_spend_tx_and_check_account_balances<F>(
     rpc_client: &RpcClient,
     sign_only: bool,
     amount: SpendAmount,
@@ -111,17 +112,21 @@ where
             0,
             compute_unit_limit,
             build_message,
-        )?;
+        )
+        .await?;
         Ok((message, spend))
     } else {
         let account = rpc_client
-            .get_account_with_commitment(from_pubkey, commitment)?
+            .get_account_with_commitment(from_pubkey, commitment)
+            .await?
             .value
             .unwrap_or_default();
         let mut from_balance = account.lamports;
         let from_rent_exempt_minimum =
             if amount == SpendAmount::RentExempt || amount == SpendAmount::Available {
-                rpc_client.get_minimum_balance_for_rent_exemption(account.data.len())?
+                rpc_client
+                    .get_minimum_balance_for_rent_exemption(account.data.len())
+                    .await?
             } else {
                 0
             };
@@ -134,7 +139,8 @@ where
                 None,
                 false,
                 None,
-            )?;
+            )
+            .await?;
             let mut subtract_rent_exempt_minimum = false;
             if let Some(active_stake) = state.active_stake {
                 from_balance = from_balance.saturating_sub(active_stake);
@@ -158,7 +164,8 @@ where
             from_rent_exempt_minimum,
             compute_unit_limit,
             build_message,
-        )?;
+        )
+        .await?;
         if from_pubkey == fee_pubkey {
             if from_balance == 0 || from_balance < spend.saturating_add(fee) {
                 return Err(CliError::InsufficientFundsForSpendAndFee(
@@ -174,7 +181,8 @@ where
                     *from_pubkey,
                 ));
             }
-            if !check_account_for_balance_with_commitment(rpc_client, fee_pubkey, fee, commitment)?
+            if !check_account_for_balance_with_commitment(rpc_client, fee_pubkey, fee, commitment)
+                .await?
             {
                 return Err(CliError::InsufficientFundsForFee(
                     build_balance_message(fee, false, false),
@@ -186,7 +194,7 @@ where
     }
 }
 
-fn resolve_spend_message<F>(
+async fn resolve_spend_message<F>(
     rpc_client: &RpcClient,
     amount: SpendAmount,
     blockhash: Option<&Hash>,
@@ -240,14 +248,15 @@ where
                         &compute_unit_limit,
                         rpc_client,
                         &mut dummy_message,
-                    )?
+                    )
+                    .await?
                 {
                     Some((ix_index, dummy_message.instructions[ix_index].data.clone()))
                 } else {
                     None
                 };
             (
-                get_fee_for_messages(rpc_client, &[&dummy_message])?,
+                get_fee_for_messages(rpc_client, &[&dummy_message]).await?,
                 compute_unit_info,
             )
         }

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 267 - 200
cli/src/stake.rs


+ 14 - 12
cli/src/test_utils.rs

@@ -1,16 +1,17 @@
 use {
     solana_clock::{Epoch, DEFAULT_MS_PER_SLOT},
     solana_commitment_config::CommitmentConfig,
-    solana_rpc_client::rpc_client::RpcClient,
+    solana_rpc_client::nonblocking::rpc_client::RpcClient,
     std::{thread::sleep, time::Duration},
 };
 
 #[macro_export]
 macro_rules! check_balance {
     ($expected_balance:expr, $client:expr, $pubkey:expr) => {
-        (0..5).for_each(|tries| {
+        for tries in 0..5 {
             let balance = $client
-                .get_balance_with_commitment($pubkey, CommitmentConfig::processed())
+                .get_balance_with_commitment(&$pubkey, CommitmentConfig::processed())
+                .await
                 .unwrap()
                 .value;
             if balance == $expected_balance {
@@ -20,16 +21,17 @@ macro_rules! check_balance {
                 assert_eq!(balance, $expected_balance);
             }
             std::thread::sleep(std::time::Duration::from_millis(500));
-        });
+        }
     };
     ($expected_balance:expr, $client:expr, $pubkey:expr,) => {
         check_balance!($expected_balance, $client, $pubkey)
     };
 }
 
-pub fn check_ready(rpc_client: &RpcClient) {
+pub async fn check_ready(rpc_client: &RpcClient) {
     while rpc_client
         .get_slot_with_commitment(CommitmentConfig::processed())
+        .await
         .unwrap()
         < 5
     {
@@ -37,27 +39,27 @@ pub fn check_ready(rpc_client: &RpcClient) {
     }
 }
 
-pub fn wait_n_slots(rpc_client: &RpcClient, n: u64) -> u64 {
-    let slot = rpc_client.get_slot().unwrap();
+pub async fn wait_n_slots(rpc_client: &RpcClient, n: u64) -> u64 {
+    let slot = rpc_client.get_slot().await.unwrap();
     loop {
         sleep(Duration::from_millis(DEFAULT_MS_PER_SLOT));
-        let new_slot = rpc_client.get_slot().unwrap();
+        let new_slot = rpc_client.get_slot().await.unwrap();
         if new_slot.saturating_sub(slot) >= n {
             return new_slot;
         }
     }
 }
 
-pub fn wait_for_next_epoch_plus_n_slots(rpc_client: &RpcClient, n: u64) -> (Epoch, u64) {
-    let current_epoch = rpc_client.get_epoch_info().unwrap().epoch;
+pub async fn wait_for_next_epoch_plus_n_slots(rpc_client: &RpcClient, n: u64) -> (Epoch, u64) {
+    let current_epoch = rpc_client.get_epoch_info().await.unwrap().epoch;
     let next_epoch = current_epoch.saturating_add(1);
     println!("waiting for epoch {next_epoch} plus {n} slots");
     loop {
         sleep(Duration::from_millis(DEFAULT_MS_PER_SLOT));
 
-        let next_epoch = rpc_client.get_epoch_info().unwrap().epoch;
+        let next_epoch = rpc_client.get_epoch_info().await.unwrap().epoch;
         if next_epoch > current_epoch {
-            let new_slot = wait_n_slots(rpc_client, n);
+            let new_slot = wait_n_slots(rpc_client, n).await;
             return (next_epoch, new_slot);
         }
     }

+ 26 - 17
cli/src/validator_info.rs

@@ -28,7 +28,7 @@ use {
     solana_message::Message,
     solana_pubkey::Pubkey,
     solana_remote_wallet::remote_wallet::RemoteWalletManager,
-    solana_rpc_client::rpc_client::RpcClient,
+    solana_rpc_client::nonblocking::rpc_client::RpcClient,
     solana_signer::Signer,
     solana_transaction::Transaction,
     std::{error, rc::Rc},
@@ -271,9 +271,9 @@ pub fn parse_get_validator_info_command(
     ))
 }
 
-pub fn process_set_validator_info(
+pub async fn process_set_validator_info(
     rpc_client: &RpcClient,
-    config: &CliConfig,
+    config: &CliConfig<'_>,
     validator_info: &Value,
     force_keybase: bool,
     info_pubkey: Option<Pubkey>,
@@ -305,7 +305,9 @@ pub fn process_set_validator_info(
     }
 
     // Check for existing validator-info account
-    let all_config = rpc_client.get_program_accounts(&solana_config_interface::id())?;
+    let all_config = rpc_client
+        .get_program_accounts(&solana_config_interface::id())
+        .await?;
     let existing_account = all_config
         .iter()
         .filter(
@@ -330,7 +332,7 @@ pub fn process_set_validator_info(
     };
 
     // Check existence of validator-info account
-    let balance = rpc_client.get_balance(&info_pubkey).unwrap_or(0);
+    let balance = rpc_client.get_balance(&info_pubkey).await.unwrap_or(0);
 
     let keys = vec![
         (validator_info::id(), false),
@@ -339,7 +341,9 @@ pub fn process_set_validator_info(
     let data_len = MAX_VALIDATOR_INFO
         .checked_add(serialized_size(&ConfigKeys { keys: keys.clone() }).unwrap())
         .expect("ValidatorInfo and two keys fit into a u64");
-    let lamports = rpc_client.get_minimum_balance_for_rent_exemption(data_len as usize)?;
+    let lamports = rpc_client
+        .get_minimum_balance_for_rent_exemption(data_len as usize)
+        .await?;
 
     let signers = if balance == 0 {
         if info_pubkey != info_keypair.pubkey() {
@@ -399,7 +403,7 @@ pub fn process_set_validator_info(
     };
 
     // Submit transaction
-    let latest_blockhash = rpc_client.get_latest_blockhash()?;
+    let latest_blockhash = rpc_client.get_latest_blockhash().await?;
     let (message, _) = resolve_spend_tx_and_check_account_balance(
         rpc_client,
         false,
@@ -409,32 +413,37 @@ pub fn process_set_validator_info(
         compute_unit_limit,
         build_message,
         config.commitment,
-    )?;
+    )
+    .await?;
     let mut tx = Transaction::new_unsigned(message);
     tx.try_sign(&signers, latest_blockhash)?;
-    let signature_str = rpc_client.send_and_confirm_transaction_with_spinner_and_config(
-        &tx,
-        config.commitment,
-        config.send_transaction_config,
-    )?;
+    let signature_str = rpc_client
+        .send_and_confirm_transaction_with_spinner_and_config(
+            &tx,
+            config.commitment,
+            config.send_transaction_config,
+        )
+        .await?;
 
     println!("Success! Validator info published at: {info_pubkey:?}");
     println!("{signature_str}");
     Ok("".to_string())
 }
 
-pub fn process_get_validator_info(
+pub async fn process_get_validator_info(
     rpc_client: &RpcClient,
-    config: &CliConfig,
+    config: &CliConfig<'_>,
     pubkey: Option<Pubkey>,
 ) -> ProcessResult {
     let validator_info: Vec<(Pubkey, Account)> = if let Some(validator_info_pubkey) = pubkey {
         vec![(
             validator_info_pubkey,
-            rpc_client.get_account(&validator_info_pubkey)?,
+            rpc_client.get_account(&validator_info_pubkey).await?,
         )]
     } else {
-        let all_config = rpc_client.get_program_accounts(&solana_config_interface::id())?;
+        let all_config = rpc_client
+            .get_program_accounts(&solana_config_interface::id())
+            .await?;
         all_config
             .into_iter()
             .filter(|(_, validator_info_account)| {

+ 212 - 155
cli/src/vote.rs

@@ -30,12 +30,13 @@ use {
         CliLandedVote, CliVoteAccount, ReturnSignersConfig,
     },
     solana_commitment_config::CommitmentConfig,
+    solana_feature_gate_interface::from_account,
     solana_message::Message,
     solana_pubkey::Pubkey,
     solana_remote_wallet::remote_wallet::RemoteWalletManager,
-    solana_rpc_client::rpc_client::RpcClient,
+    solana_rpc_client::nonblocking::rpc_client::RpcClient,
     solana_rpc_client_api::config::RpcGetVoteAccountsConfig,
-    solana_rpc_client_nonce_utils::blockhash_query::BlockhashQuery,
+    solana_rpc_client_nonce_utils::nonblocking::blockhash_query::BlockhashQuery,
     solana_system_interface::error::SystemError,
     solana_transaction::Transaction,
     solana_vote_program::{
@@ -788,9 +789,9 @@ pub fn parse_close_vote_account(
 }
 
 #[allow(clippy::too_many_arguments)]
-pub fn process_create_vote_account(
+pub async fn process_create_vote_account(
     rpc_client: &RpcClient,
-    config: &CliConfig,
+    config: &CliConfig<'_>,
     vote_account: SignerIndex,
     seed: &Option<String>,
     identity_account: SignerIndex,
@@ -826,7 +827,8 @@ pub fn process_create_vote_account(
     )?;
 
     let required_balance = rpc_client
-        .get_minimum_balance_for_rent_exemption(VoteStateV4::size_of())?
+        .get_minimum_balance_for_rent_exemption(VoteStateV4::size_of())
+        .await?
         .max(1);
     let amount = SpendAmount::Some(required_balance);
 
@@ -835,8 +837,8 @@ pub fn process_create_vote_account(
     let space = VoteStateV4::size_of() as u64;
 
     let compute_unit_limit = match blockhash_query {
-        BlockhashQuery::None(_) | BlockhashQuery::FeeCalculator(_, _) => ComputeUnitLimit::Default,
-        BlockhashQuery::All(_) => ComputeUnitLimit::Simulated,
+        BlockhashQuery::Static(_) | BlockhashQuery::Validated(_, _) => ComputeUnitLimit::Default,
+        BlockhashQuery::Rpc(_) => ComputeUnitLimit::Simulated,
     };
     let build_message = |lamports| {
         let vote_init = VoteInit {
@@ -881,7 +883,9 @@ pub fn process_create_vote_account(
         }
     };
 
-    let recent_blockhash = blockhash_query.get_blockhash(rpc_client, config.commitment)?;
+    let recent_blockhash = blockhash_query
+        .get_blockhash(rpc_client, config.commitment)
+        .await?;
 
     let (message, _) = resolve_spend_tx_and_check_account_balances(
         rpc_client,
@@ -893,11 +897,13 @@ pub fn process_create_vote_account(
         compute_unit_limit,
         build_message,
         config.commitment,
-    )?;
+    )
+    .await?;
 
     if !sign_only {
-        if let Ok(response) =
-            rpc_client.get_account_with_commitment(&vote_account_address, config.commitment)
+        if let Ok(response) = rpc_client
+            .get_account_with_commitment(&vote_account_address, config.commitment)
+            .await
         {
             if let Some(vote_account) = response.value {
                 let err_msg = if vote_account.owner == solana_vote_program::id() {
@@ -912,11 +918,13 @@ pub fn process_create_vote_account(
         }
 
         if let Some(nonce_account) = &nonce_account {
-            let nonce_account = solana_rpc_client_nonce_utils::get_account_with_commitment(
-                rpc_client,
-                nonce_account,
-                config.commitment,
-            )?;
+            let nonce_account =
+                solana_rpc_client_nonce_utils::nonblocking::get_account_with_commitment(
+                    rpc_client,
+                    nonce_account,
+                    config.commitment,
+                )
+                .await?;
             check_nonce_account(&nonce_account, &nonce_authority.pubkey(), &recent_blockhash)?;
         }
     }
@@ -933,19 +941,21 @@ pub fn process_create_vote_account(
         )
     } else {
         tx.try_sign(&config.signers, recent_blockhash)?;
-        let result = rpc_client.send_and_confirm_transaction_with_spinner_and_config(
-            &tx,
-            config.commitment,
-            config.send_transaction_config,
-        );
+        let result = rpc_client
+            .send_and_confirm_transaction_with_spinner_and_config(
+                &tx,
+                config.commitment,
+                config.send_transaction_config,
+            )
+            .await;
         log_instruction_custom_error::<SystemError>(result, config)
     }
 }
 
 #[allow(clippy::too_many_arguments)]
-pub fn process_vote_authorize(
+pub async fn process_vote_authorize(
     rpc_client: &RpcClient,
-    config: &CliConfig,
+    config: &CliConfig<'_>,
     vote_account_pubkey: &Pubkey,
     new_authorized_pubkey: &Pubkey,
     vote_authorize: VoteAuthorize,
@@ -964,14 +974,18 @@ pub fn process_vote_authorize(
     let new_authorized_signer = new_authorized.map(|index| config.signers[index]);
 
     let vote_state = if !sign_only {
-        Some(get_vote_account(rpc_client, vote_account_pubkey, config.commitment)?.1)
+        Some(
+            get_vote_account(rpc_client, vote_account_pubkey, config.commitment)
+                .await?
+                .1,
+        )
     } else {
         None
     };
     match vote_authorize {
         VoteAuthorize::Voter => {
             if let Some(vote_state) = vote_state {
-                let current_epoch = rpc_client.get_epoch_info()?.epoch;
+                let current_epoch = rpc_client.get_epoch_info().await?.epoch;
                 let current_authorized_voter = vote_state
                     .authorized_voters
                     .get_authorized_voter(current_epoch)
@@ -1023,8 +1037,8 @@ pub fn process_vote_authorize(
     };
 
     let compute_unit_limit = match blockhash_query {
-        BlockhashQuery::None(_) | BlockhashQuery::FeeCalculator(_, _) => ComputeUnitLimit::Default,
-        BlockhashQuery::All(_) => ComputeUnitLimit::Simulated,
+        BlockhashQuery::Static(_) | BlockhashQuery::Validated(_, _) => ComputeUnitLimit::Default,
+        BlockhashQuery::Rpc(_) => ComputeUnitLimit::Simulated,
     };
     let ixs = vec![vote_ix]
         .with_memo(memo)
@@ -1033,7 +1047,9 @@ pub fn process_vote_authorize(
             compute_unit_limit,
         });
 
-    let recent_blockhash = blockhash_query.get_blockhash(rpc_client, config.commitment)?;
+    let recent_blockhash = blockhash_query
+        .get_blockhash(rpc_client, config.commitment)
+        .await?;
 
     let nonce_authority = config.signers[nonce_authority];
     let fee_payer = config.signers[fee_payer];
@@ -1048,7 +1064,7 @@ pub fn process_vote_authorize(
     } else {
         Message::new(&ixs, Some(&fee_payer.pubkey()))
     };
-    simulate_and_update_compute_unit_limit(&compute_unit_limit, rpc_client, &mut message)?;
+    simulate_and_update_compute_unit_limit(&compute_unit_limit, rpc_client, &mut message).await?;
     let mut tx = Transaction::new_unsigned(message);
 
     if sign_only {
@@ -1063,11 +1079,13 @@ pub fn process_vote_authorize(
     } else {
         tx.try_sign(&config.signers, recent_blockhash)?;
         if let Some(nonce_account) = &nonce_account {
-            let nonce_account = solana_rpc_client_nonce_utils::get_account_with_commitment(
-                rpc_client,
-                nonce_account,
-                config.commitment,
-            )?;
+            let nonce_account =
+                solana_rpc_client_nonce_utils::nonblocking::get_account_with_commitment(
+                    rpc_client,
+                    nonce_account,
+                    config.commitment,
+                )
+                .await?;
             check_nonce_account(&nonce_account, &nonce_authority.pubkey(), &recent_blockhash)?;
         }
         check_account_for_fee_with_commitment(
@@ -1075,20 +1093,23 @@ pub fn process_vote_authorize(
             &config.signers[0].pubkey(),
             &tx.message,
             config.commitment,
-        )?;
-        let result = rpc_client.send_and_confirm_transaction_with_spinner_and_config(
-            &tx,
-            config.commitment,
-            config.send_transaction_config,
-        );
+        )
+        .await?;
+        let result = rpc_client
+            .send_and_confirm_transaction_with_spinner_and_config(
+                &tx,
+                config.commitment,
+                config.send_transaction_config,
+            )
+            .await;
         log_instruction_custom_error::<VoteError>(result, config)
     }
 }
 
 #[allow(clippy::too_many_arguments)]
-pub fn process_vote_update_validator(
+pub async fn process_vote_update_validator(
     rpc_client: &RpcClient,
-    config: &CliConfig,
+    config: &CliConfig<'_>,
     vote_account_pubkey: &Pubkey,
     new_identity_account: SignerIndex,
     withdraw_authority: SignerIndex,
@@ -1108,10 +1129,12 @@ pub fn process_vote_update_validator(
         (vote_account_pubkey, "vote_account_pubkey".to_string()),
         (&new_identity_pubkey, "new_identity_account".to_string()),
     )?;
-    let recent_blockhash = blockhash_query.get_blockhash(rpc_client, config.commitment)?;
+    let recent_blockhash = blockhash_query
+        .get_blockhash(rpc_client, config.commitment)
+        .await?;
     let compute_unit_limit = match blockhash_query {
-        BlockhashQuery::None(_) | BlockhashQuery::FeeCalculator(_, _) => ComputeUnitLimit::Default,
-        BlockhashQuery::All(_) => ComputeUnitLimit::Simulated,
+        BlockhashQuery::Static(_) | BlockhashQuery::Validated(_, _) => ComputeUnitLimit::Default,
+        BlockhashQuery::Rpc(_) => ComputeUnitLimit::Simulated,
     };
     let ixs = vec![vote_instruction::update_validator_identity(
         vote_account_pubkey,
@@ -1136,7 +1159,7 @@ pub fn process_vote_update_validator(
     } else {
         Message::new(&ixs, Some(&fee_payer.pubkey()))
     };
-    simulate_and_update_compute_unit_limit(&compute_unit_limit, rpc_client, &mut message)?;
+    simulate_and_update_compute_unit_limit(&compute_unit_limit, rpc_client, &mut message).await?;
     let mut tx = Transaction::new_unsigned(message);
 
     if sign_only {
@@ -1151,11 +1174,13 @@ pub fn process_vote_update_validator(
     } else {
         tx.try_sign(&config.signers, recent_blockhash)?;
         if let Some(nonce_account) = &nonce_account {
-            let nonce_account = solana_rpc_client_nonce_utils::get_account_with_commitment(
-                rpc_client,
-                nonce_account,
-                config.commitment,
-            )?;
+            let nonce_account =
+                solana_rpc_client_nonce_utils::nonblocking::get_account_with_commitment(
+                    rpc_client,
+                    nonce_account,
+                    config.commitment,
+                )
+                .await?;
             check_nonce_account(&nonce_account, &nonce_authority.pubkey(), &recent_blockhash)?;
         }
         check_account_for_fee_with_commitment(
@@ -1163,20 +1188,23 @@ pub fn process_vote_update_validator(
             &config.signers[0].pubkey(),
             &tx.message,
             config.commitment,
-        )?;
-        let result = rpc_client.send_and_confirm_transaction_with_spinner_and_config(
-            &tx,
-            config.commitment,
-            config.send_transaction_config,
-        );
+        )
+        .await?;
+        let result = rpc_client
+            .send_and_confirm_transaction_with_spinner_and_config(
+                &tx,
+                config.commitment,
+                config.send_transaction_config,
+            )
+            .await;
         log_instruction_custom_error::<VoteError>(result, config)
     }
 }
 
 #[allow(clippy::too_many_arguments)]
-pub fn process_vote_update_commission(
+pub async fn process_vote_update_commission(
     rpc_client: &RpcClient,
-    config: &CliConfig,
+    config: &CliConfig<'_>,
     vote_account_pubkey: &Pubkey,
     commission: u8,
     withdraw_authority: SignerIndex,
@@ -1190,10 +1218,12 @@ pub fn process_vote_update_commission(
     compute_unit_price: Option<u64>,
 ) -> ProcessResult {
     let authorized_withdrawer = config.signers[withdraw_authority];
-    let recent_blockhash = blockhash_query.get_blockhash(rpc_client, config.commitment)?;
+    let recent_blockhash = blockhash_query
+        .get_blockhash(rpc_client, config.commitment)
+        .await?;
     let compute_unit_limit = match blockhash_query {
-        BlockhashQuery::None(_) | BlockhashQuery::FeeCalculator(_, _) => ComputeUnitLimit::Default,
-        BlockhashQuery::All(_) => ComputeUnitLimit::Simulated,
+        BlockhashQuery::Static(_) | BlockhashQuery::Validated(_, _) => ComputeUnitLimit::Default,
+        BlockhashQuery::Rpc(_) => ComputeUnitLimit::Simulated,
     };
     let ixs = vec![vote_instruction::update_commission(
         vote_account_pubkey,
@@ -1218,7 +1248,7 @@ pub fn process_vote_update_commission(
     } else {
         Message::new(&ixs, Some(&fee_payer.pubkey()))
     };
-    simulate_and_update_compute_unit_limit(&compute_unit_limit, rpc_client, &mut message)?;
+    simulate_and_update_compute_unit_limit(&compute_unit_limit, rpc_client, &mut message).await?;
     let mut tx = Transaction::new_unsigned(message);
     if sign_only {
         tx.try_partial_sign(&config.signers, recent_blockhash)?;
@@ -1232,11 +1262,13 @@ pub fn process_vote_update_commission(
     } else {
         tx.try_sign(&config.signers, recent_blockhash)?;
         if let Some(nonce_account) = &nonce_account {
-            let nonce_account = solana_rpc_client_nonce_utils::get_account_with_commitment(
-                rpc_client,
-                nonce_account,
-                config.commitment,
-            )?;
+            let nonce_account =
+                solana_rpc_client_nonce_utils::nonblocking::get_account_with_commitment(
+                    rpc_client,
+                    nonce_account,
+                    config.commitment,
+                )
+                .await?;
             check_nonce_account(&nonce_account, &nonce_authority.pubkey(), &recent_blockhash)?;
         }
         check_account_for_fee_with_commitment(
@@ -1244,23 +1276,27 @@ pub fn process_vote_update_commission(
             &config.signers[0].pubkey(),
             &tx.message,
             config.commitment,
-        )?;
-        let result = rpc_client.send_and_confirm_transaction_with_spinner_and_config(
-            &tx,
-            config.commitment,
-            config.send_transaction_config,
-        );
+        )
+        .await?;
+        let result = rpc_client
+            .send_and_confirm_transaction_with_spinner_and_config(
+                &tx,
+                config.commitment,
+                config.send_transaction_config,
+            )
+            .await;
         log_instruction_custom_error::<VoteError>(result, config)
     }
 }
 
-pub(crate) fn get_vote_account(
+pub(crate) async fn get_vote_account(
     rpc_client: &RpcClient,
     vote_account_pubkey: &Pubkey,
     commitment_config: CommitmentConfig,
 ) -> Result<(Account, VoteStateV4), Box<dyn std::error::Error>> {
     let vote_account = rpc_client
-        .get_account_with_commitment(vote_account_pubkey, commitment_config)?
+        .get_account_with_commitment(vote_account_pubkey, commitment_config)
+        .await?
         .value
         .ok_or_else(|| {
             CliError::RpcRequestError(format!("{vote_account_pubkey:?} account does not exist"))
@@ -1282,9 +1318,9 @@ pub(crate) fn get_vote_account(
     Ok((vote_account, vote_state))
 }
 
-pub fn process_show_vote_account(
+pub async fn process_show_vote_account(
     rpc_client: &RpcClient,
-    config: &CliConfig,
+    config: &CliConfig<'_>,
     vote_account_address: &Pubkey,
     use_lamports_unit: bool,
     use_csv: bool,
@@ -1292,11 +1328,19 @@ pub fn process_show_vote_account(
     starting_epoch: Option<u64>,
 ) -> ProcessResult {
     let (vote_account, vote_state) =
-        get_vote_account(rpc_client, vote_account_address, config.commitment)?;
+        get_vote_account(rpc_client, vote_account_address, config.commitment).await?;
 
-    let epoch_schedule = rpc_client.get_epoch_schedule()?;
-    let tvc_activation_slot =
-        rpc_client.get_feature_activation_slot(&agave_feature_set::timely_vote_credits::id())?;
+    let epoch_schedule = rpc_client.get_epoch_schedule().await?;
+    let tvc_activation_slot = rpc_client
+        .get_account_with_commitment(
+            &agave_feature_set::timely_vote_credits::id(),
+            config.commitment,
+        )
+        .await
+        .ok()
+        .and_then(|response| response.value)
+        .and_then(|account| from_account(&account))
+        .and_then(|feature| feature.activated_at);
     let tvc_activation_epoch = tvc_activation_slot.map(|s| epoch_schedule.get_epoch(s));
 
     let mut votes: Vec<CliLandedVote> = vec![];
@@ -1325,21 +1369,24 @@ pub fn process_show_vote_account(
         }
     }
 
-    let epoch_rewards =
-        with_rewards.and_then(|num_epochs| {
-            match crate::stake::fetch_epoch_rewards(
-                rpc_client,
-                vote_account_address,
-                num_epochs,
-                starting_epoch,
-            ) {
-                Ok(rewards) => Some(rewards),
-                Err(error) => {
-                    eprintln!("Failed to fetch epoch rewards: {error:?}");
-                    None
-                }
+    let epoch_rewards = if let Some(num_epochs) = with_rewards {
+        match crate::stake::fetch_epoch_rewards(
+            rpc_client,
+            vote_account_address,
+            num_epochs,
+            starting_epoch,
+        )
+        .await
+        {
+            Ok(rewards) => Some(rewards),
+            Err(error) => {
+                eprintln!("Failed to fetch epoch rewards: {error:?}");
+                None
             }
-        });
+        }
+    } else {
+        None
+    };
 
     let vote_account_data = CliVoteAccount {
         account_balance: vote_account.lamports,
@@ -1369,9 +1416,9 @@ pub fn process_show_vote_account(
 }
 
 #[allow(clippy::too_many_arguments)]
-pub fn process_withdraw_from_vote_account(
+pub async fn process_withdraw_from_vote_account(
     rpc_client: &RpcClient,
-    config: &CliConfig,
+    config: &CliConfig<'_>,
     vote_account_pubkey: &Pubkey,
     withdraw_authority: SignerIndex,
     withdraw_amount: SpendAmount,
@@ -1386,14 +1433,16 @@ pub fn process_withdraw_from_vote_account(
     compute_unit_price: Option<u64>,
 ) -> ProcessResult {
     let withdraw_authority = config.signers[withdraw_authority];
-    let recent_blockhash = blockhash_query.get_blockhash(rpc_client, config.commitment)?;
+    let recent_blockhash = blockhash_query
+        .get_blockhash(rpc_client, config.commitment)
+        .await?;
 
     let fee_payer = config.signers[fee_payer];
     let nonce_authority = config.signers[nonce_authority];
 
     let compute_unit_limit = match blockhash_query {
-        BlockhashQuery::None(_) | BlockhashQuery::FeeCalculator(_, _) => ComputeUnitLimit::Default,
-        BlockhashQuery::All(_) => ComputeUnitLimit::Simulated,
+        BlockhashQuery::Static(_) | BlockhashQuery::Validated(_, _) => ComputeUnitLimit::Default,
+        BlockhashQuery::Rpc(_) => ComputeUnitLimit::Simulated,
     };
     let build_message = |lamports| {
         let ixs = vec![withdraw(
@@ -1430,12 +1479,14 @@ pub fn process_withdraw_from_vote_account(
         compute_unit_limit,
         build_message,
         config.commitment,
-    )?;
+    )
+    .await?;
 
     if !sign_only {
-        let current_balance = rpc_client.get_balance(vote_account_pubkey)?;
-        let minimum_balance =
-            rpc_client.get_minimum_balance_for_rent_exemption(VoteStateV4::size_of())?;
+        let current_balance = rpc_client.get_balance(vote_account_pubkey).await?;
+        let minimum_balance = rpc_client
+            .get_minimum_balance_for_rent_exemption(VoteStateV4::size_of())
+            .await?;
         if let SpendAmount::Some(withdraw_amount) = withdraw_amount {
             let balance_remaining = current_balance.saturating_sub(withdraw_amount);
             if balance_remaining < minimum_balance && balance_remaining != 0 {
@@ -1463,11 +1514,13 @@ pub fn process_withdraw_from_vote_account(
     } else {
         tx.try_sign(&config.signers, recent_blockhash)?;
         if let Some(nonce_account) = &nonce_account {
-            let nonce_account = solana_rpc_client_nonce_utils::get_account_with_commitment(
-                rpc_client,
-                nonce_account,
-                config.commitment,
-            )?;
+            let nonce_account =
+                solana_rpc_client_nonce_utils::nonblocking::get_account_with_commitment(
+                    rpc_client,
+                    nonce_account,
+                    config.commitment,
+                )
+                .await?;
             check_nonce_account(&nonce_account, &nonce_authority.pubkey(), &recent_blockhash)?;
         }
         check_account_for_fee_with_commitment(
@@ -1475,19 +1528,22 @@ pub fn process_withdraw_from_vote_account(
             &tx.message.account_keys[0],
             &tx.message,
             config.commitment,
-        )?;
-        let result = rpc_client.send_and_confirm_transaction_with_spinner_and_config(
-            &tx,
-            config.commitment,
-            config.send_transaction_config,
-        );
+        )
+        .await?;
+        let result = rpc_client
+            .send_and_confirm_transaction_with_spinner_and_config(
+                &tx,
+                config.commitment,
+                config.send_transaction_config,
+            )
+            .await;
         log_instruction_custom_error::<VoteError>(result, config)
     }
 }
 
-pub fn process_close_vote_account(
+pub async fn process_close_vote_account(
     rpc_client: &RpcClient,
-    config: &CliConfig,
+    config: &CliConfig<'_>,
     vote_account_pubkey: &Pubkey,
     withdraw_authority: SignerIndex,
     destination_account_pubkey: &Pubkey,
@@ -1495,11 +1551,12 @@ pub fn process_close_vote_account(
     fee_payer: SignerIndex,
     compute_unit_price: Option<u64>,
 ) -> ProcessResult {
-    let vote_account_status =
-        rpc_client.get_vote_accounts_with_config(RpcGetVoteAccountsConfig {
+    let vote_account_status = rpc_client
+        .get_vote_accounts_with_config(RpcGetVoteAccountsConfig {
             vote_pubkey: Some(vote_account_pubkey.to_string()),
             ..RpcGetVoteAccountsConfig::default()
-        })?;
+        })
+        .await?;
 
     if let Some(vote_account) = vote_account_status
         .current
@@ -1515,11 +1572,11 @@ pub fn process_close_vote_account(
         }
     }
 
-    let latest_blockhash = rpc_client.get_latest_blockhash()?;
+    let latest_blockhash = rpc_client.get_latest_blockhash().await?;
     let withdraw_authority = config.signers[withdraw_authority];
     let fee_payer = config.signers[fee_payer];
 
-    let current_balance = rpc_client.get_balance(vote_account_pubkey)?;
+    let current_balance = rpc_client.get_balance(vote_account_pubkey).await?;
 
     let compute_unit_limit = ComputeUnitLimit::Simulated;
     let ixs = vec![withdraw(
@@ -1535,7 +1592,7 @@ pub fn process_close_vote_account(
     });
 
     let mut message = Message::new(&ixs, Some(&fee_payer.pubkey()));
-    simulate_and_update_compute_unit_limit(&compute_unit_limit, rpc_client, &mut message)?;
+    simulate_and_update_compute_unit_limit(&compute_unit_limit, rpc_client, &mut message).await?;
     let mut tx = Transaction::new_unsigned(message);
     tx.try_sign(&config.signers, latest_blockhash)?;
     check_account_for_fee_with_commitment(
@@ -1543,12 +1600,15 @@ pub fn process_close_vote_account(
         &tx.message.account_keys[0],
         &tx.message,
         config.commitment,
-    )?;
-    let result = rpc_client.send_and_confirm_transaction_with_spinner_and_config(
-        &tx,
-        config.commitment,
-        config.send_transaction_config,
-    );
+    )
+    .await?;
+    let result = rpc_client
+        .send_and_confirm_transaction_with_spinner_and_config(
+            &tx,
+            config.commitment,
+            config.send_transaction_config,
+        )
+        .await;
     log_instruction_custom_error::<VoteError>(result, config)
 }
 
@@ -1560,7 +1620,7 @@ mod tests {
         solana_hash::Hash,
         solana_keypair::{read_keypair_file, write_keypair, Keypair},
         solana_presigner::Presigner,
-        solana_rpc_client_nonce_utils::blockhash_query,
+        solana_rpc_client_nonce_utils::nonblocking::blockhash_query::Source,
         solana_signer::Signer,
         tempfile::NamedTempFile,
     };
@@ -1608,7 +1668,7 @@ mod tests {
                     vote_authorize: VoteAuthorize::Voter,
                     sign_only: false,
                     dump_transaction_message: false,
-                    blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
+                    blockhash_query: BlockhashQuery::Rpc(Source::Cluster),
                     nonce_account: None,
                     nonce_authority: 0,
                     memo: None,
@@ -1641,7 +1701,7 @@ mod tests {
                     vote_authorize: VoteAuthorize::Voter,
                     sign_only: false,
                     dump_transaction_message: false,
-                    blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
+                    blockhash_query: BlockhashQuery::Rpc(Source::Cluster),
                     nonce_account: None,
                     nonce_authority: 0,
                     memo: None,
@@ -1676,7 +1736,7 @@ mod tests {
                     vote_authorize: VoteAuthorize::Voter,
                     sign_only: true,
                     dump_transaction_message: false,
-                    blockhash_query: BlockhashQuery::None(blockhash),
+                    blockhash_query: BlockhashQuery::Static(blockhash),
                     nonce_account: None,
                     nonce_authority: 0,
                     memo: None,
@@ -1722,8 +1782,8 @@ mod tests {
                     vote_authorize: VoteAuthorize::Voter,
                     sign_only: false,
                     dump_transaction_message: false,
-                    blockhash_query: BlockhashQuery::FeeCalculator(
-                        blockhash_query::Source::NonceAccount(nonce_account),
+                    blockhash_query: BlockhashQuery::Validated(
+                        Source::NonceAccount(nonce_account),
                         blockhash
                     ),
                     nonce_account: Some(nonce_account),
@@ -1765,7 +1825,7 @@ mod tests {
                     vote_authorize: VoteAuthorize::Voter,
                     sign_only: false,
                     dump_transaction_message: false,
-                    blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
+                    blockhash_query: BlockhashQuery::Rpc(Source::Cluster),
                     nonce_account: None,
                     nonce_authority: 0,
                     memo: None,
@@ -1797,7 +1857,7 @@ mod tests {
                     vote_authorize: VoteAuthorize::Voter,
                     sign_only: false,
                     dump_transaction_message: false,
-                    blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
+                    blockhash_query: BlockhashQuery::Rpc(Source::Cluster),
                     nonce_account: None,
                     nonce_authority: 0,
                     memo: None,
@@ -1853,7 +1913,7 @@ mod tests {
                     commission: 10,
                     sign_only: false,
                     dump_transaction_message: false,
-                    blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
+                    blockhash_query: BlockhashQuery::Rpc(Source::Cluster),
                     nonce_account: None,
                     nonce_authority: 0,
                     memo: None,
@@ -1887,7 +1947,7 @@ mod tests {
                     commission: 100,
                     sign_only: false,
                     dump_transaction_message: false,
-                    blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
+                    blockhash_query: BlockhashQuery::Rpc(Source::Cluster),
                     nonce_account: None,
                     nonce_authority: 0,
                     memo: None,
@@ -1928,7 +1988,7 @@ mod tests {
                     commission: 10,
                     sign_only: true,
                     dump_transaction_message: false,
-                    blockhash_query: BlockhashQuery::None(blockhash),
+                    blockhash_query: BlockhashQuery::Static(blockhash),
                     nonce_account: None,
                     nonce_authority: 0,
                     memo: None,
@@ -1978,8 +2038,8 @@ mod tests {
                     commission: 10,
                     sign_only: false,
                     dump_transaction_message: false,
-                    blockhash_query: BlockhashQuery::FeeCalculator(
-                        blockhash_query::Source::NonceAccount(nonce_account),
+                    blockhash_query: BlockhashQuery::Validated(
+                        Source::NonceAccount(nonce_account),
                         blockhash
                     ),
                     nonce_account: Some(nonce_account),
@@ -2024,7 +2084,7 @@ mod tests {
                     commission: 100,
                     sign_only: false,
                     dump_transaction_message: false,
-                    blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
+                    blockhash_query: BlockhashQuery::Rpc(Source::Cluster),
                     nonce_account: None,
                     nonce_authority: 0,
                     memo: None,
@@ -2063,7 +2123,7 @@ mod tests {
                     commission: 100,
                     sign_only: false,
                     dump_transaction_message: false,
-                    blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
+                    blockhash_query: BlockhashQuery::Rpc(Source::Cluster),
                     nonce_account: None,
                     nonce_authority: 0,
                     memo: None,
@@ -2094,7 +2154,7 @@ mod tests {
                     withdraw_authority: 1,
                     sign_only: false,
                     dump_transaction_message: false,
-                    blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
+                    blockhash_query: BlockhashQuery::Rpc(Source::Cluster),
                     nonce_account: None,
                     nonce_authority: 0,
                     memo: None,
@@ -2125,7 +2185,7 @@ mod tests {
                     withdraw_authority: 1,
                     sign_only: false,
                     dump_transaction_message: false,
-                    blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
+                    blockhash_query: BlockhashQuery::Rpc(Source::Cluster),
                     nonce_account: None,
                     nonce_authority: 0,
                     memo: None,
@@ -2157,7 +2217,7 @@ mod tests {
                     withdraw_amount: SpendAmount::Some(42_000_000_000),
                     sign_only: false,
                     dump_transaction_message: false,
-                    blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
+                    blockhash_query: BlockhashQuery::Rpc(Source::Cluster),
                     nonce_account: None,
                     nonce_authority: 0,
                     memo: None,
@@ -2186,7 +2246,7 @@ mod tests {
                     withdraw_amount: SpendAmount::RentExempt,
                     sign_only: false,
                     dump_transaction_message: false,
-                    blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
+                    blockhash_query: BlockhashQuery::Rpc(Source::Cluster),
                     nonce_account: None,
                     nonce_authority: 0,
                     memo: None,
@@ -2220,7 +2280,7 @@ mod tests {
                     withdraw_amount: SpendAmount::Some(42_000_000_000),
                     sign_only: false,
                     dump_transaction_message: false,
-                    blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
+                    blockhash_query: BlockhashQuery::Rpc(Source::Cluster),
                     nonce_account: None,
                     nonce_authority: 0,
                     memo: None,
@@ -2259,7 +2319,7 @@ mod tests {
                     withdraw_amount: SpendAmount::Some(42_000_000_000),
                     sign_only: true,
                     dump_transaction_message: false,
-                    blockhash_query: BlockhashQuery::None(blockhash),
+                    blockhash_query: BlockhashQuery::Static(blockhash),
                     nonce_account: None,
                     nonce_authority: 0,
                     memo: None,
@@ -2299,10 +2359,7 @@ mod tests {
                     withdraw_amount: SpendAmount::Some(42_000_000_000),
                     sign_only: false,
                     dump_transaction_message: false,
-                    blockhash_query: BlockhashQuery::FeeCalculator(
-                        blockhash_query::Source::Cluster,
-                        blockhash
-                    ),
+                    blockhash_query: BlockhashQuery::Validated(Source::Cluster, blockhash),
                     nonce_account: None,
                     nonce_authority: 0,
                     memo: None,

+ 61 - 45
cli/src/wallet.rs

@@ -33,9 +33,9 @@ use {
     solana_offchain_message::OffchainMessage,
     solana_pubkey::Pubkey,
     solana_remote_wallet::remote_wallet::RemoteWalletManager,
-    solana_rpc_client::rpc_client::RpcClient,
+    solana_rpc_client::nonblocking::rpc_client::RpcClient,
     solana_rpc_client_api::config::RpcTransactionConfig,
-    solana_rpc_client_nonce_utils::blockhash_query::BlockhashQuery,
+    solana_rpc_client_nonce_utils::nonblocking::blockhash_query::BlockhashQuery,
     solana_sdk_ids::{stake, system_program},
     solana_signature::Signature,
     solana_system_interface::{error::SystemError, instruction as system_instruction},
@@ -647,14 +647,14 @@ pub fn parse_verify_offchain_signature(
     })
 }
 
-pub fn process_show_account(
+pub async fn process_show_account(
     rpc_client: &RpcClient,
-    config: &CliConfig,
+    config: &CliConfig<'_>,
     account_pubkey: &Pubkey,
     output_file: &Option<String>,
     use_lamports_unit: bool,
 ) -> ProcessResult {
-    let account = rpc_client.get_account(account_pubkey)?;
+    let account = rpc_client.get_account(account_pubkey).await?;
     let data = &account.data;
     let cli_account = CliAccount::new(account_pubkey, &account, use_lamports_unit);
 
@@ -686,9 +686,9 @@ pub fn process_show_account(
     Ok(account_string)
 }
 
-pub fn process_airdrop(
+pub async fn process_airdrop(
     rpc_client: &RpcClient,
-    config: &CliConfig,
+    config: &CliConfig<'_>,
     pubkey: &Option<Pubkey>,
     lamports: u64,
 ) -> ProcessResult {
@@ -702,14 +702,14 @@ pub fn process_airdrop(
         build_balance_message(lamports, false, true),
     );
 
-    let pre_balance = rpc_client.get_balance(&pubkey)?;
+    let pre_balance = rpc_client.get_balance(&pubkey).await?;
 
-    let result = request_and_confirm_airdrop(rpc_client, config, &pubkey, lamports);
+    let result = request_and_confirm_airdrop(rpc_client, config, &pubkey, lamports).await;
     if let Ok(signature) = result {
         let signature_cli_message = log_instruction_custom_error::<SystemError>(result, config)?;
         println!("{signature_cli_message}");
 
-        let current_balance = rpc_client.get_balance(&pubkey)?;
+        let current_balance = rpc_client.get_balance(&pubkey).await?;
 
         if current_balance < pre_balance.saturating_add(lamports) {
             println!("Balance unchanged");
@@ -723,9 +723,9 @@ pub fn process_airdrop(
     }
 }
 
-pub fn process_balance(
+pub async fn process_balance(
     rpc_client: &RpcClient,
-    config: &CliConfig,
+    config: &CliConfig<'_>,
     pubkey: &Option<Pubkey>,
     use_lamports_unit: bool,
 ) -> ProcessResult {
@@ -734,7 +734,7 @@ pub fn process_balance(
     } else {
         config.pubkey()?
     };
-    let balance = rpc_client.get_balance(&pubkey)?;
+    let balance = rpc_client.get_balance(&pubkey).await?;
     let balance_output = CliBalance {
         lamports: balance,
         config: BuildBalanceMessageConfig {
@@ -747,25 +747,31 @@ pub fn process_balance(
     Ok(config.output_format.formatted_string(&balance_output))
 }
 
-pub fn process_confirm(
+pub async fn process_confirm(
     rpc_client: &RpcClient,
-    config: &CliConfig,
+    config: &CliConfig<'_>,
     signature: &Signature,
 ) -> ProcessResult {
-    match rpc_client.get_signature_statuses_with_history(&[*signature]) {
+    match rpc_client
+        .get_signature_statuses_with_history(&[*signature])
+        .await
+    {
         Ok(status) => {
             let cli_transaction = if let Some(transaction_status) = &status.value[0] {
                 let mut transaction = None;
                 let mut get_transaction_error = None;
                 if config.verbose {
-                    match rpc_client.get_transaction_with_config(
-                        signature,
-                        RpcTransactionConfig {
-                            encoding: Some(UiTransactionEncoding::Base64),
-                            commitment: Some(CommitmentConfig::confirmed()),
-                            max_supported_transaction_version: Some(0),
-                        },
-                    ) {
+                    match rpc_client
+                        .get_transaction_with_config(
+                            signature,
+                            RpcTransactionConfig {
+                                encoding: Some(UiTransactionEncoding::Base64),
+                                commitment: Some(CommitmentConfig::confirmed()),
+                                max_supported_transaction_version: Some(0),
+                            },
+                        )
+                        .await
+                    {
                         Ok(confirmed_transaction) => {
                             let EncodedConfirmedTransactionWithStatusMeta {
                                 block_time,
@@ -813,7 +819,7 @@ pub fn process_confirm(
 }
 
 pub fn process_decode_transaction(
-    config: &CliConfig,
+    config: &CliConfig<'_>,
     transaction: &VersionedTransaction,
 ) -> ProcessResult {
     let sigverify_status = CliSignatureVerificationStatus::verify_transaction(transaction);
@@ -830,7 +836,7 @@ pub fn process_decode_transaction(
 }
 
 pub fn process_create_address_with_seed(
-    config: &CliConfig,
+    config: &CliConfig<'_>,
     from_pubkey: Option<&Pubkey>,
     seed: &str,
     program_id: &Pubkey,
@@ -845,7 +851,7 @@ pub fn process_create_address_with_seed(
 }
 
 pub fn process_find_program_derived_address(
-    config: &CliConfig,
+    config: &CliConfig<'_>,
     seeds: &Vec<Vec<u8>>,
     program_id: &Pubkey,
 ) -> ProcessResult {
@@ -862,9 +868,9 @@ pub fn process_find_program_derived_address(
 }
 
 #[allow(clippy::too_many_arguments)]
-pub fn process_transfer(
+pub async fn process_transfer(
     rpc_client: &RpcClient,
-    config: &CliConfig,
+    config: &CliConfig<'_>,
     amount: SpendAmount,
     to: &Pubkey,
     from: SignerIndex,
@@ -884,11 +890,14 @@ pub fn process_transfer(
     let from = config.signers[from];
     let mut from_pubkey = from.pubkey();
 
-    let recent_blockhash = blockhash_query.get_blockhash(rpc_client, config.commitment)?;
+    let recent_blockhash = blockhash_query
+        .get_blockhash(rpc_client, config.commitment)
+        .await?;
 
     if !sign_only && !allow_unfunded_recipient {
         let recipient_balance = rpc_client
-            .get_balance_with_commitment(to, config.commitment)?
+            .get_balance_with_commitment(to, config.commitment)
+            .await?
             .value;
         if recipient_balance == 0 {
             return Err(format!(
@@ -962,7 +971,8 @@ pub fn process_transfer(
         compute_unit_limit,
         build_message,
         config.commitment,
-    )?;
+    )
+    .await?;
     let mut tx = Transaction::new_unsigned(message);
 
     if sign_only {
@@ -976,37 +986,43 @@ pub fn process_transfer(
         )
     } else {
         if let Some(nonce_account) = &nonce_account {
-            let nonce_account = solana_rpc_client_nonce_utils::get_account_with_commitment(
-                rpc_client,
-                nonce_account,
-                config.commitment,
-            )?;
+            let nonce_account =
+                solana_rpc_client_nonce_utils::nonblocking::get_account_with_commitment(
+                    rpc_client,
+                    nonce_account,
+                    config.commitment,
+                )
+                .await?;
             check_nonce_account(&nonce_account, &nonce_authority.pubkey(), &recent_blockhash)?;
         }
 
         tx.try_sign(&config.signers, recent_blockhash)?;
         let result = if no_wait {
-            rpc_client.send_transaction_with_config(&tx, config.send_transaction_config)
+            rpc_client
+                .send_transaction_with_config(&tx, config.send_transaction_config)
+                .await
         } else {
-            rpc_client.send_and_confirm_transaction_with_spinner_and_config(
-                &tx,
-                config.commitment,
-                config.send_transaction_config,
-            )
+            rpc_client
+                .send_and_confirm_transaction_with_spinner_and_config(
+                    &tx,
+                    config.commitment,
+                    config.send_transaction_config,
+                )
+                .await
         };
         log_instruction_custom_error::<SystemError>(result, config)
     }
 }
 
 pub fn process_sign_offchain_message(
-    config: &CliConfig,
+    config: &CliConfig<'_>,
     message: &OffchainMessage,
 ) -> ProcessResult {
     Ok(message.sign(config.signers[0])?.to_string())
 }
 
 pub fn process_verify_offchain_signature(
-    config: &CliConfig,
+    config: &CliConfig<'_>,
     signer_pubkey: &Option<Pubkey>,
     signature: &Signature,
     message: &OffchainMessage,

+ 32 - 26
cli/tests/address_lookup_table.rs

@@ -17,13 +17,16 @@ use {
     std::str::FromStr,
 };
 
-#[test]
-fn test_cli_create_extend_and_freeze_address_lookup_table() {
+#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
+async fn test_cli_create_extend_and_freeze_address_lookup_table() {
     let mint_keypair = Keypair::new();
-    let mint_pubkey = mint_keypair.pubkey();
-    let faucet_addr = run_local_faucet_with_unique_port_for_tests(mint_keypair);
-    let test_validator =
-        TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified);
+    let faucet_addr = run_local_faucet_with_unique_port_for_tests(mint_keypair.insecure_clone());
+    let test_validator = TestValidator::async_with_no_fees(
+        &mint_keypair,
+        Some(faucet_addr),
+        SocketAddrSpace::Unspecified,
+    )
+    .await;
 
     let mut config = CliConfig::recent_for_tests();
     let keypair = Keypair::new();
@@ -36,7 +39,7 @@ fn test_cli_create_extend_and_freeze_address_lookup_table() {
         pubkey: None,
         lamports: 10 * LAMPORTS_PER_SOL,
     };
-    process_command(&config).unwrap();
+    process_command(&config).await.unwrap();
 
     // Create lookup table
     config.command =
@@ -45,7 +48,7 @@ fn test_cli_create_extend_and_freeze_address_lookup_table() {
             payer_signer_index: 0,
         });
     let response: CliAddressLookupTableCreated =
-        serde_json::from_str(&process_command(&config).unwrap()).unwrap();
+        serde_json::from_str(&process_command(&config).await.unwrap()).unwrap();
     let lookup_table_pubkey = Pubkey::from_str(&response.lookup_table_address).unwrap();
 
     // Validate created lookup table
@@ -55,7 +58,7 @@ fn test_cli_create_extend_and_freeze_address_lookup_table() {
                 lookup_table_pubkey,
             });
         let response: CliAddressLookupTable =
-            serde_json::from_str(&process_command(&config).unwrap()).unwrap();
+            serde_json::from_str(&process_command(&config).await.unwrap()).unwrap();
         assert_eq!(
             response,
             CliAddressLookupTable {
@@ -77,7 +80,7 @@ fn test_cli_create_extend_and_freeze_address_lookup_table() {
             payer_signer_index: 0,
             new_addresses: new_addresses.clone(),
         });
-    process_command(&config).unwrap();
+    process_command(&config).await.unwrap();
 
     // Validate extended lookup table
     {
@@ -89,7 +92,7 @@ fn test_cli_create_extend_and_freeze_address_lookup_table() {
             addresses,
             last_extended_slot,
             ..
-        } = serde_json::from_str(&process_command(&config).unwrap()).unwrap();
+        } = serde_json::from_str(&process_command(&config).await.unwrap()).unwrap();
         assert_eq!(
             addresses
                 .into_iter()
@@ -107,7 +110,7 @@ fn test_cli_create_extend_and_freeze_address_lookup_table() {
             authority_signer_index: 0,
             bypass_warning: false,
         });
-    let process_err = process_command(&config).unwrap_err();
+    let process_err = process_command(&config).await.unwrap_err();
     assert_eq!(process_err.to_string(), FREEZE_LOOKUP_TABLE_WARNING);
 
     // Freeze lookup table w/ bypass
@@ -117,7 +120,7 @@ fn test_cli_create_extend_and_freeze_address_lookup_table() {
             authority_signer_index: 0,
             bypass_warning: true,
         });
-    process_command(&config).unwrap();
+    process_command(&config).await.unwrap();
 
     // Validate frozen lookup table
     {
@@ -126,18 +129,21 @@ fn test_cli_create_extend_and_freeze_address_lookup_table() {
                 lookup_table_pubkey,
             });
         let CliAddressLookupTable { authority, .. } =
-            serde_json::from_str(&process_command(&config).unwrap()).unwrap();
+            serde_json::from_str(&process_command(&config).await.unwrap()).unwrap();
         assert!(authority.is_none());
     }
 }
 
-#[test]
-fn test_cli_create_and_deactivate_address_lookup_table() {
+#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
+async fn test_cli_create_and_deactivate_address_lookup_table() {
     let mint_keypair = Keypair::new();
-    let mint_pubkey = mint_keypair.pubkey();
-    let faucet_addr = run_local_faucet_with_unique_port_for_tests(mint_keypair);
-    let test_validator =
-        TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified);
+    let faucet_addr = run_local_faucet_with_unique_port_for_tests(mint_keypair.insecure_clone());
+    let test_validator = TestValidator::async_with_no_fees(
+        &mint_keypair,
+        Some(faucet_addr),
+        SocketAddrSpace::Unspecified,
+    )
+    .await;
 
     let mut config = CliConfig::recent_for_tests();
     let keypair = Keypair::new();
@@ -150,7 +156,7 @@ fn test_cli_create_and_deactivate_address_lookup_table() {
         pubkey: None,
         lamports: 10 * LAMPORTS_PER_SOL,
     };
-    process_command(&config).unwrap();
+    process_command(&config).await.unwrap();
 
     // Create lookup table
     config.command =
@@ -159,7 +165,7 @@ fn test_cli_create_and_deactivate_address_lookup_table() {
             payer_signer_index: 0,
         });
     let response: CliAddressLookupTableCreated =
-        serde_json::from_str(&process_command(&config).unwrap()).unwrap();
+        serde_json::from_str(&process_command(&config).await.unwrap()).unwrap();
     let lookup_table_pubkey = Pubkey::from_str(&response.lookup_table_address).unwrap();
 
     // Validate created lookup table
@@ -169,7 +175,7 @@ fn test_cli_create_and_deactivate_address_lookup_table() {
                 lookup_table_pubkey,
             });
         let response: CliAddressLookupTable =
-            serde_json::from_str(&process_command(&config).unwrap()).unwrap();
+            serde_json::from_str(&process_command(&config).await.unwrap()).unwrap();
         assert_eq!(
             response,
             CliAddressLookupTable {
@@ -189,7 +195,7 @@ fn test_cli_create_and_deactivate_address_lookup_table() {
             authority_signer_index: 0,
             bypass_warning: false,
         });
-    let process_err = process_command(&config).unwrap_err();
+    let process_err = process_command(&config).await.unwrap_err();
     assert_eq!(process_err.to_string(), DEACTIVATE_LOOKUP_TABLE_WARNING);
 
     // Deactivate lookup table w/ bypass
@@ -199,7 +205,7 @@ fn test_cli_create_and_deactivate_address_lookup_table() {
             authority_signer_index: 0,
             bypass_warning: true,
         });
-    process_command(&config).unwrap();
+    process_command(&config).await.unwrap();
 
     // Validate deactivated lookup table
     {
@@ -209,7 +215,7 @@ fn test_cli_create_and_deactivate_address_lookup_table() {
             });
         let CliAddressLookupTable {
             deactivation_slot, ..
-        } = serde_json::from_str(&process_command(&config).unwrap()).unwrap();
+        } = serde_json::from_str(&process_command(&config).await.unwrap()).unwrap();
         assert_ne!(deactivation_slot, u64::MAX);
     }
 }

+ 19 - 12
cli/tests/cluster_query.rs

@@ -10,27 +10,30 @@ use {
     solana_keypair::Keypair,
     solana_native_token::LAMPORTS_PER_SOL,
     solana_net_utils::SocketAddrSpace,
-    solana_rpc_client::rpc_client::RpcClient,
+    solana_rpc_client::nonblocking::rpc_client::RpcClient,
     solana_signer::Signer,
     solana_test_validator::TestValidator,
     std::time::Duration,
     test_case::test_case,
 };
 
-#[test_case(None; "base")]
-#[test_case(Some(1_000_000); "with_compute_unit_price")]
-fn test_ping(compute_unit_price: Option<u64>) {
+#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
+#[test_case(false, None; "rpc_base")]
+#[test_case(false, Some(1_000_000); "rpc_with_compute_unit_price")]
+#[test_case(true, None; "tpu_base")]
+#[test_case(true, Some(1_000_000); "tpu_with_compute_unit_price")]
+async fn test_ping(use_tpu_client: bool, compute_unit_price: Option<u64>) {
     agave_logger::setup();
     let fee = FeeStructure::default().get_max_fee(1, 0);
     let mint_keypair = Keypair::new();
-    let mint_pubkey = mint_keypair.pubkey();
-    let faucet_addr = run_local_faucet_with_unique_port_for_tests(mint_keypair);
-    let test_validator = TestValidator::with_custom_fees(
-        mint_pubkey,
+    let faucet_addr = run_local_faucet_with_unique_port_for_tests(mint_keypair.insecure_clone());
+    let test_validator = TestValidator::async_with_custom_fees(
+        &mint_keypair,
         fee,
         Some(faucet_addr),
         SocketAddrSpace::Unspecified,
-    );
+    )
+    .await;
 
     let rpc_client =
         RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
@@ -40,11 +43,15 @@ fn test_ping(compute_unit_price: Option<u64>) {
 
     let mut config = CliConfig::recent_for_tests();
     config.json_rpc_url = test_validator.rpc_url();
+    config.websocket_url = test_validator.rpc_pubsub_url();
     config.signers = vec![&default_signer];
+    config.use_tpu_client = use_tpu_client;
 
-    request_and_confirm_airdrop(&rpc_client, &config, &signer_pubkey, LAMPORTS_PER_SOL).unwrap();
+    request_and_confirm_airdrop(&rpc_client, &config, &signer_pubkey, LAMPORTS_PER_SOL)
+        .await
+        .unwrap();
     check_balance!(LAMPORTS_PER_SOL, &rpc_client, &signer_pubkey);
-    check_ready(&rpc_client);
+    check_ready(&rpc_client).await;
 
     let count = 5;
     config.command = CliCommand::Ping {
@@ -55,5 +62,5 @@ fn test_ping(compute_unit_price: Option<u64>) {
         print_timestamp: false,
         compute_unit_price,
     };
-    process_command(&config).unwrap();
+    process_command(&config).await.unwrap();
 }

+ 46 - 34
cli/tests/nonce.rs

@@ -14,24 +14,32 @@ use {
     solana_native_token::LAMPORTS_PER_SOL,
     solana_net_utils::SocketAddrSpace,
     solana_pubkey::Pubkey,
-    solana_rpc_client::rpc_client::RpcClient,
-    solana_rpc_client_nonce_utils::blockhash_query::{self, BlockhashQuery},
+    solana_rpc_client::nonblocking::rpc_client::RpcClient,
+    solana_rpc_client_nonce_utils::nonblocking::blockhash_query::{BlockhashQuery, Source},
     solana_signer::Signer,
     solana_system_interface::program as system_program,
     solana_test_validator::TestValidator,
     test_case::test_case,
 };
 
+#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
 #[test_case(None, false, None; "base")]
 #[test_case(Some(String::from("seed")), false, None; "with_seed")]
 #[test_case(None, true, None; "with_authority")]
 #[test_case(None, false, Some(1_000_000); "with_compute_unit_price")]
-fn test_nonce(seed: Option<String>, use_nonce_authority: bool, compute_unit_price: Option<u64>) {
+async fn test_nonce(
+    seed: Option<String>,
+    use_nonce_authority: bool,
+    compute_unit_price: Option<u64>,
+) {
     let mint_keypair = Keypair::new();
-    let mint_pubkey = mint_keypair.pubkey();
-    let faucet_addr = run_local_faucet_with_unique_port_for_tests(mint_keypair);
-    let test_validator =
-        TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified);
+    let faucet_addr = run_local_faucet_with_unique_port_for_tests(mint_keypair.insecure_clone());
+    let test_validator = TestValidator::async_with_no_fees(
+        &mint_keypair,
+        Some(faucet_addr),
+        SocketAddrSpace::Unspecified,
+    )
+    .await;
 
     let rpc_client =
         RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
@@ -48,6 +56,7 @@ fn test_nonce(seed: Option<String>, use_nonce_authority: bool, compute_unit_pric
         &config_payer.signers[0].pubkey(),
         2000 * LAMPORTS_PER_SOL,
     )
+    .await
     .unwrap();
     check_balance!(
         2000 * LAMPORTS_PER_SOL,
@@ -89,7 +98,7 @@ fn test_nonce(seed: Option<String>, use_nonce_authority: bool, compute_unit_pric
         compute_unit_price,
     };
 
-    process_command(&config_payer).unwrap();
+    process_command(&config_payer).await.unwrap();
     check_balance!(
         1000 * LAMPORTS_PER_SOL,
         &rpc_client,
@@ -100,12 +109,12 @@ fn test_nonce(seed: Option<String>, use_nonce_authority: bool, compute_unit_pric
     // Get nonce
     config_payer.signers.pop();
     config_payer.command = CliCommand::GetNonce(nonce_account);
-    let first_nonce_string = process_command(&config_payer).unwrap();
+    let first_nonce_string = process_command(&config_payer).await.unwrap();
     let first_nonce = first_nonce_string.parse::<Hash>().unwrap();
 
     // Get nonce
     config_payer.command = CliCommand::GetNonce(nonce_account);
-    let second_nonce_string = process_command(&config_payer).unwrap();
+    let second_nonce_string = process_command(&config_payer).await.unwrap();
     let second_nonce = second_nonce_string.parse::<Hash>().unwrap();
 
     assert_eq!(first_nonce, second_nonce);
@@ -126,12 +135,12 @@ fn test_nonce(seed: Option<String>, use_nonce_authority: bool, compute_unit_pric
         memo: None,
         compute_unit_price,
     };
-    process_command(&config_payer).unwrap();
+    process_command(&config_payer).await.unwrap();
 
     // Get nonce
     config_payer.signers = vec![&payer];
     config_payer.command = CliCommand::GetNonce(nonce_account);
-    let third_nonce_string = process_command(&config_payer).unwrap();
+    let third_nonce_string = process_command(&config_payer).await.unwrap();
     let third_nonce = third_nonce_string.parse::<Hash>().unwrap();
 
     assert_ne!(first_nonce, third_nonce);
@@ -147,7 +156,7 @@ fn test_nonce(seed: Option<String>, use_nonce_authority: bool, compute_unit_pric
         lamports: 100 * LAMPORTS_PER_SOL,
         compute_unit_price,
     };
-    process_command(&config_payer).unwrap();
+    process_command(&config_payer).await.unwrap();
     check_balance!(
         1000 * LAMPORTS_PER_SOL,
         &rpc_client,
@@ -161,7 +170,7 @@ fn test_nonce(seed: Option<String>, use_nonce_authority: bool, compute_unit_pric
         nonce_account_pubkey: nonce_account,
         use_lamports_unit: true,
     };
-    process_command(&config_payer).unwrap();
+    process_command(&config_payer).await.unwrap();
 
     // Set new authority
     let new_authority = Keypair::new();
@@ -172,7 +181,7 @@ fn test_nonce(seed: Option<String>, use_nonce_authority: bool, compute_unit_pric
         new_authority: new_authority.pubkey(),
         compute_unit_price,
     };
-    process_command(&config_payer).unwrap();
+    process_command(&config_payer).await.unwrap();
 
     // Old authority fails now
     config_payer.command = CliCommand::NewNonce {
@@ -181,7 +190,7 @@ fn test_nonce(seed: Option<String>, use_nonce_authority: bool, compute_unit_pric
         memo: None,
         compute_unit_price,
     };
-    process_command(&config_payer).unwrap_err();
+    process_command(&config_payer).await.unwrap_err();
 
     // New authority can advance nonce
     config_payer.signers = vec![&payer, &new_authority];
@@ -191,7 +200,7 @@ fn test_nonce(seed: Option<String>, use_nonce_authority: bool, compute_unit_pric
         memo: None,
         compute_unit_price,
     };
-    process_command(&config_payer).unwrap();
+    process_command(&config_payer).await.unwrap();
 
     // New authority can withdraw from nonce account
     config_payer.command = CliCommand::WithdrawFromNonceAccount {
@@ -202,7 +211,7 @@ fn test_nonce(seed: Option<String>, use_nonce_authority: bool, compute_unit_pric
         lamports: 100 * LAMPORTS_PER_SOL,
         compute_unit_price,
     };
-    process_command(&config_payer).unwrap();
+    process_command(&config_payer).await.unwrap();
     check_balance!(
         1000 * LAMPORTS_PER_SOL,
         &rpc_client,
@@ -212,19 +221,19 @@ fn test_nonce(seed: Option<String>, use_nonce_authority: bool, compute_unit_pric
     check_balance!(200 * LAMPORTS_PER_SOL, &rpc_client, &payee_pubkey);
 }
 
-#[test]
-fn test_create_account_with_seed() {
+#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
+async fn test_create_account_with_seed() {
     const ONE_SIG_FEE: u64 = 5000;
     agave_logger::setup();
     let mint_keypair = Keypair::new();
-    let mint_pubkey = mint_keypair.pubkey();
-    let faucet_addr = run_local_faucet_with_unique_port_for_tests(mint_keypair);
-    let test_validator = TestValidator::with_custom_fees(
-        mint_pubkey,
+    let faucet_addr = run_local_faucet_with_unique_port_for_tests(mint_keypair.insecure_clone());
+    let test_validator = TestValidator::async_with_custom_fees(
+        &mint_keypair,
         ONE_SIG_FEE,
         Some(faucet_addr),
         SocketAddrSpace::Unspecified,
-    );
+    )
+    .await;
 
     let offline_nonce_authority_signer = keypair_from_seed(&[1u8; 32]).unwrap();
     let online_nonce_creator_signer = keypair_from_seed(&[2u8; 32]).unwrap();
@@ -239,6 +248,7 @@ fn test_create_account_with_seed() {
         &offline_nonce_authority_signer.pubkey(),
         42 * LAMPORTS_PER_SOL,
     )
+    .await
     .unwrap();
     request_and_confirm_airdrop(
         &rpc_client,
@@ -246,6 +256,7 @@ fn test_create_account_with_seed() {
         &online_nonce_creator_signer.pubkey(),
         4242 * LAMPORTS_PER_SOL,
     )
+    .await
     .unwrap();
     check_balance!(
         42 * LAMPORTS_PER_SOL,
@@ -259,7 +270,7 @@ fn test_create_account_with_seed() {
     );
     check_balance!(0, &rpc_client, &to_address);
 
-    check_ready(&rpc_client);
+    check_ready(&rpc_client).await;
 
     // Create nonce account
     let creator_pubkey = online_nonce_creator_signer.pubkey();
@@ -280,7 +291,7 @@ fn test_create_account_with_seed() {
         amount: SpendAmount::Some(241 * LAMPORTS_PER_SOL),
         compute_unit_price: None,
     };
-    process_command(&creator_config).unwrap();
+    process_command(&creator_config).await.unwrap();
     check_balance!(241 * LAMPORTS_PER_SOL, &rpc_client, &nonce_address);
     check_balance!(
         42 * LAMPORTS_PER_SOL,
@@ -295,11 +306,12 @@ fn test_create_account_with_seed() {
     check_balance!(0, &rpc_client, &to_address);
 
     // Fetch nonce hash
-    let nonce_hash = solana_rpc_client_nonce_utils::get_account_with_commitment(
+    let nonce_hash = solana_rpc_client_nonce_utils::nonblocking::get_account_with_commitment(
         &rpc_client,
         &nonce_address,
         CommitmentConfig::processed(),
     )
+    .await
     .and_then(|ref a| solana_rpc_client_nonce_utils::data_from_account(a))
     .unwrap()
     .blockhash();
@@ -310,7 +322,7 @@ fn test_create_account_with_seed() {
     authority_config.signers = vec![&offline_nonce_authority_signer];
     // Verify we cannot contact the cluster
     authority_config.command = CliCommand::ClusterVersion;
-    process_command(&authority_config).unwrap_err();
+    process_command(&authority_config).await.unwrap_err();
     authority_config.command = CliCommand::Transfer {
         amount: SpendAmount::Some(10 * LAMPORTS_PER_SOL),
         to: to_address,
@@ -319,7 +331,7 @@ fn test_create_account_with_seed() {
         dump_transaction_message: true,
         allow_unfunded_recipient: true,
         no_wait: false,
-        blockhash_query: BlockhashQuery::None(nonce_hash),
+        blockhash_query: BlockhashQuery::Static(nonce_hash),
         nonce_account: Some(nonce_address),
         nonce_authority: 0,
         memo: None,
@@ -329,7 +341,7 @@ fn test_create_account_with_seed() {
         compute_unit_price: None,
     };
     authority_config.output_format = OutputFormat::JsonCompact;
-    let sign_only_reply = process_command(&authority_config).unwrap();
+    let sign_only_reply = process_command(&authority_config).await.unwrap();
     let sign_only = parse_sign_only_reply_string(&sign_only_reply);
     let authority_presigner = sign_only.presigner_of(&authority_pubkey).unwrap();
     assert_eq!(sign_only.blockhash, nonce_hash);
@@ -346,8 +358,8 @@ fn test_create_account_with_seed() {
         dump_transaction_message: true,
         allow_unfunded_recipient: true,
         no_wait: false,
-        blockhash_query: BlockhashQuery::FeeCalculator(
-            blockhash_query::Source::NonceAccount(nonce_address),
+        blockhash_query: BlockhashQuery::Validated(
+            Source::NonceAccount(nonce_address),
             sign_only.blockhash,
         ),
         nonce_account: Some(nonce_address),
@@ -358,7 +370,7 @@ fn test_create_account_with_seed() {
         derived_address_program_id: None,
         compute_unit_price: None,
     };
-    process_command(&submit_config).unwrap();
+    process_command(&submit_config).await.unwrap();
     check_balance!(241 * LAMPORTS_PER_SOL, &rpc_client, &nonce_address);
     check_balance!(
         32 * LAMPORTS_PER_SOL - ONE_SIG_FEE,

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 171 - 125
cli/tests/program.rs


+ 12 - 9
cli/tests/request_airdrop.rs

@@ -6,18 +6,20 @@ use {
     solana_keypair::Keypair,
     solana_native_token::LAMPORTS_PER_SOL,
     solana_net_utils::SocketAddrSpace,
-    solana_rpc_client::rpc_client::RpcClient,
-    solana_signer::Signer,
+    solana_rpc_client::nonblocking::rpc_client::RpcClient,
     solana_test_validator::TestValidator,
 };
 
-#[test]
-fn test_cli_request_airdrop() {
+#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
+async fn test_cli_request_airdrop() {
     let mint_keypair = Keypair::new();
-    let mint_pubkey = mint_keypair.pubkey();
-    let faucet_addr = run_local_faucet_with_unique_port_for_tests(mint_keypair);
-    let test_validator =
-        TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified);
+    let faucet_addr = run_local_faucet_with_unique_port_for_tests(mint_keypair.insecure_clone());
+    let test_validator = TestValidator::async_with_no_fees(
+        &mint_keypair,
+        Some(faucet_addr),
+        SocketAddrSpace::Unspecified,
+    )
+    .await;
 
     let mut bob_config = CliConfig::recent_for_tests();
     bob_config.json_rpc_url = test_validator.rpc_url();
@@ -28,7 +30,7 @@ fn test_cli_request_airdrop() {
     let keypair = Keypair::new();
     bob_config.signers = vec![&keypair];
 
-    let sig_response = process_command(&bob_config);
+    let sig_response = process_command(&bob_config).await;
     sig_response.unwrap();
 
     let rpc_client =
@@ -36,6 +38,7 @@ fn test_cli_request_airdrop() {
 
     let balance = rpc_client
         .get_balance(&bob_config.signers[0].pubkey())
+        .await
         .unwrap();
     assert_eq!(balance, 50 * LAMPORTS_PER_SOL);
 }

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 181 - 137
cli/tests/stake.rs


+ 106 - 84
cli/tests/transfer.rs

@@ -7,6 +7,7 @@ use {
         test_utils::check_ready,
     },
     solana_cli_output::{parse_sign_only_reply_string, OutputFormat},
+    solana_client::nonblocking::blockhash_query::Source,
     solana_commitment_config::CommitmentConfig,
     solana_compute_budget_interface::ComputeBudgetInstruction,
     solana_faucet::faucet::run_local_faucet_with_unique_port_for_tests,
@@ -17,8 +18,8 @@ use {
     solana_net_utils::SocketAddrSpace,
     solana_nonce::state::State as NonceState,
     solana_pubkey::Pubkey,
-    solana_rpc_client::rpc_client::RpcClient,
-    solana_rpc_client_nonce_utils::blockhash_query::{self, BlockhashQuery},
+    solana_rpc_client::nonblocking::rpc_client::RpcClient,
+    solana_rpc_client_nonce_utils::nonblocking::blockhash_query::BlockhashQuery,
     solana_signer::{null_signer::NullSigner, Signer},
     solana_stake_interface as stake,
     solana_system_interface::instruction as system_instruction,
@@ -26,21 +27,22 @@ use {
     test_case::test_case,
 };
 
+#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
 #[test_case(true; "Skip Preflight")]
 #[test_case(false; "Don`t skip Preflight")]
-fn test_transfer(skip_preflight: bool) {
+async fn test_transfer(skip_preflight: bool) {
     agave_logger::setup();
     let fee_one_sig = FeeStructure::default().get_max_fee(1, 0);
     let fee_two_sig = FeeStructure::default().get_max_fee(2, 0);
     let mint_keypair = Keypair::new();
-    let mint_pubkey = mint_keypair.pubkey();
-    let faucet_addr = run_local_faucet_with_unique_port_for_tests(mint_keypair);
-    let test_validator = TestValidator::with_custom_fees(
-        mint_pubkey,
+    let faucet_addr = run_local_faucet_with_unique_port_for_tests(mint_keypair.insecure_clone());
+    let test_validator = TestValidator::async_with_custom_fees(
+        &mint_keypair,
         fee_one_sig,
         Some(faucet_addr),
         SocketAddrSpace::Unspecified,
-    );
+    )
+    .await;
 
     let rpc_client =
         RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
@@ -57,11 +59,12 @@ fn test_transfer(skip_preflight: bool) {
     let recipient_pubkey = Pubkey::from([1u8; 32]);
 
     request_and_confirm_airdrop(&rpc_client, &config, &sender_pubkey, 5 * LAMPORTS_PER_SOL)
+        .await
         .unwrap();
     check_balance!(5 * LAMPORTS_PER_SOL, &rpc_client, &sender_pubkey);
     check_balance!(0, &rpc_client, &recipient_pubkey);
 
-    check_ready(&rpc_client);
+    check_ready(&rpc_client).await;
 
     // Plain ole transfer
     config.command = CliCommand::Transfer {
@@ -72,7 +75,7 @@ fn test_transfer(skip_preflight: bool) {
         dump_transaction_message: false,
         allow_unfunded_recipient: true,
         no_wait: false,
-        blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
+        blockhash_query: BlockhashQuery::Rpc(Source::Cluster),
         nonce_account: None,
         nonce_authority: 0,
         memo: None,
@@ -81,7 +84,7 @@ fn test_transfer(skip_preflight: bool) {
         derived_address_program_id: None,
         compute_unit_price: None,
     };
-    process_command(&config).unwrap();
+    process_command(&config).await.unwrap();
     check_balance!(
         4 * LAMPORTS_PER_SOL - fee_one_sig,
         &rpc_client,
@@ -98,7 +101,7 @@ fn test_transfer(skip_preflight: bool) {
         dump_transaction_message: false,
         allow_unfunded_recipient: true,
         no_wait: false,
-        blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
+        blockhash_query: BlockhashQuery::Rpc(Source::Cluster),
         nonce_account: None,
         nonce_authority: 0,
         memo: None,
@@ -107,7 +110,7 @@ fn test_transfer(skip_preflight: bool) {
         derived_address_program_id: None,
         compute_unit_price: None,
     };
-    assert!(process_command(&config).is_err());
+    assert!(process_command(&config).await.is_err());
     check_balance!(
         4 * LAMPORTS_PER_SOL - fee_one_sig,
         &rpc_client,
@@ -120,14 +123,16 @@ fn test_transfer(skip_preflight: bool) {
     offline.signers = vec![&default_offline_signer];
     // Verify we cannot contact the cluster
     offline.command = CliCommand::ClusterVersion;
-    process_command(&offline).unwrap_err();
+    process_command(&offline).await.unwrap_err();
 
     let offline_pubkey = offline.signers[0].pubkey();
-    request_and_confirm_airdrop(&rpc_client, &offline, &offline_pubkey, LAMPORTS_PER_SOL).unwrap();
+    request_and_confirm_airdrop(&rpc_client, &offline, &offline_pubkey, LAMPORTS_PER_SOL)
+        .await
+        .unwrap();
     check_balance!(LAMPORTS_PER_SOL, &rpc_client, &offline_pubkey);
 
     // Offline transfer
-    let blockhash = rpc_client.get_latest_blockhash().unwrap();
+    let blockhash = rpc_client.get_latest_blockhash().await.unwrap();
     offline.command = CliCommand::Transfer {
         amount: SpendAmount::Some(LAMPORTS_PER_SOL / 2),
         to: recipient_pubkey,
@@ -136,7 +141,7 @@ fn test_transfer(skip_preflight: bool) {
         dump_transaction_message: false,
         allow_unfunded_recipient: true,
         no_wait: false,
-        blockhash_query: BlockhashQuery::None(blockhash),
+        blockhash_query: BlockhashQuery::Static(blockhash),
         nonce_account: None,
         nonce_authority: 0,
         memo: None,
@@ -146,7 +151,7 @@ fn test_transfer(skip_preflight: bool) {
         compute_unit_price: None,
     };
     offline.output_format = OutputFormat::JsonCompact;
-    let sign_only_reply = process_command(&offline).unwrap();
+    let sign_only_reply = process_command(&offline).await.unwrap();
     let sign_only = parse_sign_only_reply_string(&sign_only_reply);
     assert!(sign_only.has_all_signers());
     let offline_presigner = sign_only.presigner_of(&offline_pubkey).unwrap();
@@ -159,7 +164,7 @@ fn test_transfer(skip_preflight: bool) {
         dump_transaction_message: false,
         allow_unfunded_recipient: true,
         no_wait: false,
-        blockhash_query: BlockhashQuery::FeeCalculator(blockhash_query::Source::Cluster, blockhash),
+        blockhash_query: BlockhashQuery::Validated(Source::Cluster, blockhash),
         nonce_account: None,
         nonce_authority: 0,
         memo: None,
@@ -168,7 +173,7 @@ fn test_transfer(skip_preflight: bool) {
         derived_address_program_id: None,
         compute_unit_price: None,
     };
-    process_command(&config).unwrap();
+    process_command(&config).await.unwrap();
     check_balance!(
         LAMPORTS_PER_SOL / 2 - fee_one_sig,
         &rpc_client,
@@ -180,6 +185,7 @@ fn test_transfer(skip_preflight: bool) {
     let nonce_account = keypair_from_seed(&[3u8; 32]).unwrap();
     let minimum_nonce_balance = rpc_client
         .get_minimum_balance_for_rent_exemption(NonceState::size())
+        .await
         .unwrap();
     config.signers = vec![&default_signer, &nonce_account];
     config.command = CliCommand::CreateNonceAccount {
@@ -190,7 +196,7 @@ fn test_transfer(skip_preflight: bool) {
         amount: SpendAmount::Some(minimum_nonce_balance),
         compute_unit_price: None,
     };
-    process_command(&config).unwrap();
+    process_command(&config).await.unwrap();
     check_balance!(
         4 * LAMPORTS_PER_SOL - fee_one_sig - fee_two_sig - minimum_nonce_balance,
         &rpc_client,
@@ -198,11 +204,12 @@ fn test_transfer(skip_preflight: bool) {
     );
 
     // Fetch nonce hash
-    let nonce_hash = solana_rpc_client_nonce_utils::get_account_with_commitment(
+    let nonce_hash = solana_rpc_client_nonce_utils::nonblocking::get_account_with_commitment(
         &rpc_client,
         &nonce_account.pubkey(),
         CommitmentConfig::processed(),
     )
+    .await
     .and_then(|ref a| solana_rpc_client_nonce_utils::data_from_account(a))
     .unwrap()
     .blockhash();
@@ -217,8 +224,8 @@ fn test_transfer(skip_preflight: bool) {
         dump_transaction_message: false,
         allow_unfunded_recipient: true,
         no_wait: false,
-        blockhash_query: BlockhashQuery::FeeCalculator(
-            blockhash_query::Source::NonceAccount(nonce_account.pubkey()),
+        blockhash_query: BlockhashQuery::Validated(
+            Source::NonceAccount(nonce_account.pubkey()),
             nonce_hash,
         ),
         nonce_account: Some(nonce_account.pubkey()),
@@ -229,18 +236,19 @@ fn test_transfer(skip_preflight: bool) {
         derived_address_program_id: None,
         compute_unit_price: None,
     };
-    process_command(&config).unwrap();
+    process_command(&config).await.unwrap();
     check_balance!(
         3 * LAMPORTS_PER_SOL - 2 * fee_one_sig - fee_two_sig - minimum_nonce_balance,
         &rpc_client,
         &sender_pubkey,
     );
     check_balance!(2_500_000_000, &rpc_client, &recipient_pubkey);
-    let new_nonce_hash = solana_rpc_client_nonce_utils::get_account_with_commitment(
+    let new_nonce_hash = solana_rpc_client_nonce_utils::nonblocking::get_account_with_commitment(
         &rpc_client,
         &nonce_account.pubkey(),
         CommitmentConfig::processed(),
     )
+    .await
     .and_then(|ref a| solana_rpc_client_nonce_utils::data_from_account(a))
     .unwrap()
     .blockhash();
@@ -255,7 +263,7 @@ fn test_transfer(skip_preflight: bool) {
         new_authority: offline_pubkey,
         compute_unit_price: None,
     };
-    process_command(&config).unwrap();
+    process_command(&config).await.unwrap();
     check_balance!(
         3 * LAMPORTS_PER_SOL - 3 * fee_one_sig - fee_two_sig - minimum_nonce_balance,
         &rpc_client,
@@ -263,11 +271,12 @@ fn test_transfer(skip_preflight: bool) {
     );
 
     // Fetch nonce hash
-    let nonce_hash = solana_rpc_client_nonce_utils::get_account_with_commitment(
+    let nonce_hash = solana_rpc_client_nonce_utils::nonblocking::get_account_with_commitment(
         &rpc_client,
         &nonce_account.pubkey(),
         CommitmentConfig::processed(),
     )
+    .await
     .and_then(|ref a| solana_rpc_client_nonce_utils::data_from_account(a))
     .unwrap()
     .blockhash();
@@ -282,7 +291,7 @@ fn test_transfer(skip_preflight: bool) {
         dump_transaction_message: false,
         allow_unfunded_recipient: true,
         no_wait: false,
-        blockhash_query: BlockhashQuery::None(nonce_hash),
+        blockhash_query: BlockhashQuery::Static(nonce_hash),
         nonce_account: Some(nonce_account.pubkey()),
         nonce_authority: 0,
         memo: None,
@@ -291,7 +300,7 @@ fn test_transfer(skip_preflight: bool) {
         derived_address_program_id: None,
         compute_unit_price: None,
     };
-    let sign_only_reply = process_command(&offline).unwrap();
+    let sign_only_reply = process_command(&offline).await.unwrap();
     let sign_only = parse_sign_only_reply_string(&sign_only_reply);
     assert!(sign_only.has_all_signers());
     let offline_presigner = sign_only.presigner_of(&offline_pubkey).unwrap();
@@ -304,8 +313,8 @@ fn test_transfer(skip_preflight: bool) {
         dump_transaction_message: false,
         allow_unfunded_recipient: true,
         no_wait: false,
-        blockhash_query: BlockhashQuery::FeeCalculator(
-            blockhash_query::Source::NonceAccount(nonce_account.pubkey()),
+        blockhash_query: BlockhashQuery::Validated(
+            Source::NonceAccount(nonce_account.pubkey()),
             sign_only.blockhash,
         ),
         nonce_account: Some(nonce_account.pubkey()),
@@ -316,7 +325,7 @@ fn test_transfer(skip_preflight: bool) {
         derived_address_program_id: None,
         compute_unit_price: None,
     };
-    process_command(&config).unwrap();
+    process_command(&config).await.unwrap();
     check_balance!(
         LAMPORTS_PER_SOL / 10 - 2 * fee_one_sig,
         &rpc_client,
@@ -325,20 +334,20 @@ fn test_transfer(skip_preflight: bool) {
     check_balance!(2_900_000_000, &rpc_client, &recipient_pubkey);
 }
 
-#[test]
-fn test_transfer_multisession_signing() {
+#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
+async fn test_transfer_multisession_signing() {
     agave_logger::setup();
     let fee_one_sig = FeeStructure::default().get_max_fee(1, 0);
     let fee_two_sig = FeeStructure::default().get_max_fee(2, 0);
     let mint_keypair = Keypair::new();
-    let mint_pubkey = mint_keypair.pubkey();
-    let faucet_addr = run_local_faucet_with_unique_port_for_tests(mint_keypair);
-    let test_validator = TestValidator::with_custom_fees(
-        mint_pubkey,
+    let faucet_addr = run_local_faucet_with_unique_port_for_tests(mint_keypair.insecure_clone());
+    let test_validator = TestValidator::async_with_custom_fees(
+        &mint_keypair,
         fee_one_sig,
         Some(faucet_addr),
         SocketAddrSpace::Unspecified,
-    );
+    )
+    .await;
 
     let to_pubkey = Pubkey::from([1u8; 32]);
     let offline_from_signer = keypair_from_seed(&[2u8; 32]).unwrap();
@@ -354,6 +363,7 @@ fn test_transfer_multisession_signing() {
         &offline_from_signer.pubkey(),
         43 * LAMPORTS_PER_SOL,
     )
+    .await
     .unwrap();
     request_and_confirm_airdrop(
         &rpc_client,
@@ -361,6 +371,7 @@ fn test_transfer_multisession_signing() {
         &offline_fee_payer_signer.pubkey(),
         LAMPORTS_PER_SOL + 2 * fee_two_sig,
     )
+    .await
     .unwrap();
     check_balance!(
         43 * LAMPORTS_PER_SOL,
@@ -374,9 +385,9 @@ fn test_transfer_multisession_signing() {
     );
     check_balance!(0, &rpc_client, &to_pubkey);
 
-    check_ready(&rpc_client);
+    check_ready(&rpc_client).await;
 
-    let blockhash = rpc_client.get_latest_blockhash().unwrap();
+    let blockhash = rpc_client.get_latest_blockhash().await.unwrap();
 
     // Offline fee-payer signs first
     let mut fee_payer_config = CliConfig::recent_for_tests();
@@ -384,7 +395,7 @@ fn test_transfer_multisession_signing() {
     fee_payer_config.signers = vec![&offline_fee_payer_signer, &from_null_signer];
     // Verify we cannot contact the cluster
     fee_payer_config.command = CliCommand::ClusterVersion;
-    process_command(&fee_payer_config).unwrap_err();
+    process_command(&fee_payer_config).await.unwrap_err();
     fee_payer_config.command = CliCommand::Transfer {
         amount: SpendAmount::Some(42 * LAMPORTS_PER_SOL),
         to: to_pubkey,
@@ -393,7 +404,7 @@ fn test_transfer_multisession_signing() {
         dump_transaction_message: false,
         allow_unfunded_recipient: true,
         no_wait: false,
-        blockhash_query: BlockhashQuery::None(blockhash),
+        blockhash_query: BlockhashQuery::Static(blockhash),
         nonce_account: None,
         nonce_authority: 0,
         memo: None,
@@ -403,7 +414,7 @@ fn test_transfer_multisession_signing() {
         compute_unit_price: None,
     };
     fee_payer_config.output_format = OutputFormat::JsonCompact;
-    let sign_only_reply = process_command(&fee_payer_config).unwrap();
+    let sign_only_reply = process_command(&fee_payer_config).await.unwrap();
     let sign_only = parse_sign_only_reply_string(&sign_only_reply);
     assert!(!sign_only.has_all_signers());
     let fee_payer_presigner = sign_only
@@ -416,7 +427,7 @@ fn test_transfer_multisession_signing() {
     from_config.signers = vec![&fee_payer_presigner, &offline_from_signer];
     // Verify we cannot contact the cluster
     from_config.command = CliCommand::ClusterVersion;
-    process_command(&from_config).unwrap_err();
+    process_command(&from_config).await.unwrap_err();
     from_config.command = CliCommand::Transfer {
         amount: SpendAmount::Some(42 * LAMPORTS_PER_SOL),
         to: to_pubkey,
@@ -425,7 +436,7 @@ fn test_transfer_multisession_signing() {
         dump_transaction_message: false,
         allow_unfunded_recipient: true,
         no_wait: false,
-        blockhash_query: BlockhashQuery::None(blockhash),
+        blockhash_query: BlockhashQuery::Static(blockhash),
         nonce_account: None,
         nonce_authority: 0,
         memo: None,
@@ -435,7 +446,7 @@ fn test_transfer_multisession_signing() {
         compute_unit_price: None,
     };
     from_config.output_format = OutputFormat::JsonCompact;
-    let sign_only_reply = process_command(&from_config).unwrap();
+    let sign_only_reply = process_command(&from_config).await.unwrap();
     let sign_only = parse_sign_only_reply_string(&sign_only_reply);
     assert!(sign_only.has_all_signers());
     let from_presigner = sign_only
@@ -454,7 +465,7 @@ fn test_transfer_multisession_signing() {
         dump_transaction_message: false,
         allow_unfunded_recipient: true,
         no_wait: false,
-        blockhash_query: BlockhashQuery::FeeCalculator(blockhash_query::Source::Cluster, blockhash),
+        blockhash_query: BlockhashQuery::Validated(Source::Cluster, blockhash),
         nonce_account: None,
         nonce_authority: 0,
         memo: None,
@@ -463,7 +474,7 @@ fn test_transfer_multisession_signing() {
         derived_address_program_id: None,
         compute_unit_price: None,
     };
-    process_command(&config).unwrap();
+    process_command(&config).await.unwrap();
 
     check_balance!(LAMPORTS_PER_SOL, &rpc_client, &offline_from_signer.pubkey(),);
     check_balance!(
@@ -474,20 +485,21 @@ fn test_transfer_multisession_signing() {
     check_balance!(42 * LAMPORTS_PER_SOL, &rpc_client, &to_pubkey);
 }
 
+#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
 #[test_case(None; "default")]
 #[test_case(Some(100_000); "with_compute_unit_price")]
-fn test_transfer_all(compute_unit_price: Option<u64>) {
+async fn test_transfer_all(compute_unit_price: Option<u64>) {
     agave_logger::setup();
     let lamports_per_signature = FeeStructure::default().get_max_fee(1, 0);
     let mint_keypair = Keypair::new();
-    let mint_pubkey = mint_keypair.pubkey();
-    let faucet_addr = run_local_faucet_with_unique_port_for_tests(mint_keypair);
-    let test_validator = TestValidator::with_custom_fees(
-        mint_pubkey,
+    let faucet_addr = run_local_faucet_with_unique_port_for_tests(mint_keypair.insecure_clone());
+    let test_validator = TestValidator::async_with_custom_fees(
+        &mint_keypair,
         lamports_per_signature,
         Some(faucet_addr),
         SocketAddrSpace::Unspecified,
-    );
+    )
+    .await;
 
     let rpc_client =
         RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
@@ -512,10 +524,13 @@ fn test_transfer_all(compute_unit_price: Option<u64>) {
                 compute_unit_price,
             ));
         }
-        let blockhash = rpc_client.get_latest_blockhash().unwrap();
+        let blockhash = rpc_client.get_latest_blockhash().await.unwrap();
         let sample_message =
             Message::new_with_blockhash(&instructions, Some(&default_signer.pubkey()), &blockhash);
-        rpc_client.get_fee_for_message(&sample_message).unwrap()
+        rpc_client
+            .get_fee_for_message(&sample_message)
+            .await
+            .unwrap()
     };
 
     let mut config = CliConfig::recent_for_tests();
@@ -524,11 +539,13 @@ fn test_transfer_all(compute_unit_price: Option<u64>) {
 
     let sender_pubkey = config.signers[0].pubkey();
 
-    request_and_confirm_airdrop(&rpc_client, &config, &sender_pubkey, 500_000).unwrap();
+    request_and_confirm_airdrop(&rpc_client, &config, &sender_pubkey, 500_000)
+        .await
+        .unwrap();
     check_balance!(500_000, &rpc_client, &sender_pubkey);
     check_balance!(0, &rpc_client, &recipient_pubkey);
 
-    check_ready(&rpc_client);
+    check_ready(&rpc_client).await;
 
     // Plain ole transfer
     config.command = CliCommand::Transfer {
@@ -539,7 +556,7 @@ fn test_transfer_all(compute_unit_price: Option<u64>) {
         dump_transaction_message: false,
         allow_unfunded_recipient: true,
         no_wait: false,
-        blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
+        blockhash_query: BlockhashQuery::Rpc(Source::Cluster),
         nonce_account: None,
         nonce_authority: 0,
         memo: None,
@@ -548,23 +565,23 @@ fn test_transfer_all(compute_unit_price: Option<u64>) {
         derived_address_program_id: None,
         compute_unit_price,
     };
-    process_command(&config).unwrap();
+    process_command(&config).await.unwrap();
     check_balance!(0, &rpc_client, &sender_pubkey);
     check_balance!(500_000 - fee, &rpc_client, &recipient_pubkey);
 }
 
-#[test]
-fn test_transfer_unfunded_recipient() {
+#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
+async fn test_transfer_unfunded_recipient() {
     agave_logger::setup();
     let mint_keypair = Keypair::new();
-    let mint_pubkey = mint_keypair.pubkey();
-    let faucet_addr = run_local_faucet_with_unique_port_for_tests(mint_keypair);
-    let test_validator = TestValidator::with_custom_fees(
-        mint_pubkey,
+    let faucet_addr = run_local_faucet_with_unique_port_for_tests(mint_keypair.insecure_clone());
+    let test_validator = TestValidator::async_with_custom_fees(
+        &mint_keypair,
         1,
         Some(faucet_addr),
         SocketAddrSpace::Unspecified,
-    );
+    )
+    .await;
 
     let rpc_client =
         RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
@@ -579,11 +596,13 @@ fn test_transfer_unfunded_recipient() {
     let sender_pubkey = config.signers[0].pubkey();
     let recipient_pubkey = Pubkey::from([1u8; 32]);
 
-    request_and_confirm_airdrop(&rpc_client, &config, &sender_pubkey, 50_000).unwrap();
+    request_and_confirm_airdrop(&rpc_client, &config, &sender_pubkey, 50_000)
+        .await
+        .unwrap();
     check_balance!(50_000, &rpc_client, &sender_pubkey);
     check_balance!(0, &rpc_client, &recipient_pubkey);
 
-    check_ready(&rpc_client);
+    check_ready(&rpc_client).await;
 
     // Plain ole transfer
     config.command = CliCommand::Transfer {
@@ -594,7 +613,7 @@ fn test_transfer_unfunded_recipient() {
         dump_transaction_message: false,
         allow_unfunded_recipient: false,
         no_wait: false,
-        blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
+        blockhash_query: BlockhashQuery::Rpc(Source::Cluster),
         nonce_account: None,
         nonce_authority: 0,
         memo: None,
@@ -605,22 +624,22 @@ fn test_transfer_unfunded_recipient() {
     };
 
     // Expect failure due to unfunded recipient and the lack of the `allow_unfunded_recipient` flag
-    process_command(&config).unwrap_err();
+    process_command(&config).await.unwrap_err();
 }
 
-#[test]
-fn test_transfer_with_seed() {
+#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
+async fn test_transfer_with_seed() {
     agave_logger::setup();
     let fee = FeeStructure::default().get_max_fee(1, 0);
     let mint_keypair = Keypair::new();
-    let mint_pubkey = mint_keypair.pubkey();
-    let faucet_addr = run_local_faucet_with_unique_port_for_tests(mint_keypair);
-    let test_validator = TestValidator::with_custom_fees(
-        mint_pubkey,
+    let faucet_addr = run_local_faucet_with_unique_port_for_tests(mint_keypair.insecure_clone());
+    let test_validator = TestValidator::async_with_custom_fees(
+        &mint_keypair,
         fee,
         Some(faucet_addr),
         SocketAddrSpace::Unspecified,
-    );
+    )
+    .await;
 
     let rpc_client =
         RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
@@ -642,14 +661,17 @@ fn test_transfer_with_seed() {
     )
     .unwrap();
 
-    request_and_confirm_airdrop(&rpc_client, &config, &sender_pubkey, LAMPORTS_PER_SOL).unwrap();
+    request_and_confirm_airdrop(&rpc_client, &config, &sender_pubkey, LAMPORTS_PER_SOL)
+        .await
+        .unwrap();
     request_and_confirm_airdrop(&rpc_client, &config, &derived_address, 5 * LAMPORTS_PER_SOL)
+        .await
         .unwrap();
     check_balance!(LAMPORTS_PER_SOL, &rpc_client, &sender_pubkey);
     check_balance!(5 * LAMPORTS_PER_SOL, &rpc_client, &derived_address);
     check_balance!(0, &rpc_client, &recipient_pubkey);
 
-    check_ready(&rpc_client);
+    check_ready(&rpc_client).await;
 
     // Transfer with seed
     config.command = CliCommand::Transfer {
@@ -660,7 +682,7 @@ fn test_transfer_with_seed() {
         dump_transaction_message: false,
         allow_unfunded_recipient: true,
         no_wait: false,
-        blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
+        blockhash_query: BlockhashQuery::Rpc(Source::Cluster),
         nonce_account: None,
         nonce_authority: 0,
         memo: None,
@@ -669,7 +691,7 @@ fn test_transfer_with_seed() {
         derived_address_program_id: Some(derived_address_program_id),
         compute_unit_price: None,
     };
-    process_command(&config).unwrap();
+    process_command(&config).await.unwrap();
     check_balance!(LAMPORTS_PER_SOL - fee, &rpc_client, &sender_pubkey);
     check_balance!(5 * LAMPORTS_PER_SOL, &rpc_client, &recipient_pubkey);
     check_balance!(0, &rpc_client, &derived_address);

+ 12 - 8
cli/tests/validator_info.rs

@@ -8,22 +8,25 @@ use {
     solana_faucet::faucet::run_local_faucet_with_unique_port_for_tests,
     solana_keypair::{keypair_from_seed, Keypair},
     solana_net_utils::SocketAddrSpace,
-    solana_rpc_client::rpc_client::RpcClient,
-    solana_signer::Signer,
+    solana_rpc_client::nonblocking::rpc_client::RpcClient,
     solana_test_validator::TestValidator,
     test_case::test_case,
 };
 
+#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
 #[test_case(None; "base")]
 #[test_case(Some(1_000_000); "with_compute_unit_price")]
-fn test_publish(compute_unit_price: Option<u64>) {
+async fn test_publish(compute_unit_price: Option<u64>) {
     agave_logger::setup();
 
     let mint_keypair = Keypair::new();
-    let mint_pubkey = mint_keypair.pubkey();
-    let faucet_addr = run_local_faucet_with_unique_port_for_tests(mint_keypair);
-    let test_validator =
-        TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified);
+    let faucet_addr = run_local_faucet_with_unique_port_for_tests(mint_keypair.insecure_clone());
+    let test_validator = TestValidator::async_with_no_fees(
+        &mint_keypair,
+        Some(faucet_addr),
+        SocketAddrSpace::Unspecified,
+    )
+    .await;
 
     let rpc_client =
         RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
@@ -39,6 +42,7 @@ fn test_publish(compute_unit_price: Option<u64>) {
         &config_validator.signers[0].pubkey(),
         100_000_000_000,
     )
+    .await
     .unwrap();
     check_balance!(
         100_000_000_000,
@@ -52,5 +56,5 @@ fn test_publish(compute_unit_price: Option<u64>) {
         info_pubkey: None,
         compute_unit_price,
     };
-    process_command(&config_validator).unwrap();
+    process_command(&config_validator).await.unwrap();
 }

+ 72 - 53
cli/tests/vote.rs

@@ -7,26 +7,31 @@ use {
         spend_utils::SpendAmount,
     },
     solana_cli_output::{parse_sign_only_reply_string, OutputFormat},
+    solana_client::nonblocking::blockhash_query::Source,
     solana_commitment_config::CommitmentConfig,
     solana_faucet::faucet::run_local_faucet_with_unique_port_for_tests,
     solana_keypair::Keypair,
     solana_net_utils::SocketAddrSpace,
-    solana_rpc_client::rpc_client::RpcClient,
-    solana_rpc_client_nonce_utils::blockhash_query::{self, BlockhashQuery},
+    solana_rpc_client::nonblocking::rpc_client::RpcClient,
+    solana_rpc_client_nonce_utils::nonblocking::blockhash_query::BlockhashQuery,
     solana_signer::{null_signer::NullSigner, Signer},
     solana_test_validator::TestValidator,
     solana_vote_program::vote_state::{VoteAuthorize, VoteStateV4},
     test_case::test_case,
 };
 
+#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
 #[test_case(None; "base")]
 #[test_case(Some(1_000_000); "with_compute_unit_price")]
-fn test_vote_authorize_and_withdraw(compute_unit_price: Option<u64>) {
+async fn test_vote_authorize_and_withdraw(compute_unit_price: Option<u64>) {
     let mint_keypair = Keypair::new();
-    let mint_pubkey = mint_keypair.pubkey();
-    let faucet_addr = run_local_faucet_with_unique_port_for_tests(mint_keypair);
-    let test_validator =
-        TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified);
+    let faucet_addr = run_local_faucet_with_unique_port_for_tests(mint_keypair.insecure_clone());
+    let test_validator = TestValidator::async_with_no_fees(
+        &mint_keypair,
+        Some(faucet_addr),
+        SocketAddrSpace::Unspecified,
+    )
+    .await;
 
     let rpc_client =
         RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
@@ -37,6 +42,7 @@ fn test_vote_authorize_and_withdraw(compute_unit_price: Option<u64>) {
     config.signers = vec![&default_signer];
 
     request_and_confirm_airdrop(&rpc_client, &config, &config.signers[0].pubkey(), 100_000)
+        .await
         .unwrap();
 
     // Create vote account
@@ -52,16 +58,17 @@ fn test_vote_authorize_and_withdraw(compute_unit_price: Option<u64>) {
         commission: 0,
         sign_only: false,
         dump_transaction_message: false,
-        blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
+        blockhash_query: BlockhashQuery::Rpc(Source::Cluster),
         nonce_account: None,
         nonce_authority: 0,
         memo: None,
         fee_payer: 0,
         compute_unit_price,
     };
-    process_command(&config).unwrap();
+    process_command(&config).await.unwrap();
     let vote_account = rpc_client
         .get_account(&vote_account_keypair.pubkey())
+        .await
         .unwrap();
     let vote_state =
         VoteStateV4::deserialize(vote_account.data(), &vote_account_keypair.pubkey()).unwrap();
@@ -69,6 +76,7 @@ fn test_vote_authorize_and_withdraw(compute_unit_price: Option<u64>) {
     assert_eq!(authorized_withdrawer, config.signers[0].pubkey());
     let expected_balance = rpc_client
         .get_minimum_balance_for_rent_exemption(VoteStateV4::size_of())
+        .await
         .unwrap()
         .max(1);
     check_balance!(expected_balance, &rpc_client, &vote_account_pubkey);
@@ -83,7 +91,7 @@ fn test_vote_authorize_and_withdraw(compute_unit_price: Option<u64>) {
         dump_transaction_message: false,
         allow_unfunded_recipient: true,
         no_wait: false,
-        blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
+        blockhash_query: BlockhashQuery::Rpc(Source::Cluster),
         nonce_account: None,
         nonce_authority: 0,
         memo: None,
@@ -92,7 +100,7 @@ fn test_vote_authorize_and_withdraw(compute_unit_price: Option<u64>) {
         derived_address_program_id: None,
         compute_unit_price,
     };
-    process_command(&config).unwrap();
+    process_command(&config).await.unwrap();
     let expected_balance = expected_balance + 10_000;
     check_balance!(expected_balance, &rpc_client, &vote_account_pubkey);
 
@@ -105,7 +113,7 @@ fn test_vote_authorize_and_withdraw(compute_unit_price: Option<u64>) {
         vote_authorize: VoteAuthorize::Withdrawer,
         sign_only: false,
         dump_transaction_message: false,
-        blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
+        blockhash_query: BlockhashQuery::Rpc(Source::Cluster),
         nonce_account: None,
         nonce_authority: 0,
         memo: None,
@@ -114,9 +122,10 @@ fn test_vote_authorize_and_withdraw(compute_unit_price: Option<u64>) {
         new_authorized: None,
         compute_unit_price,
     };
-    process_command(&config).unwrap();
+    process_command(&config).await.unwrap();
     let vote_account = rpc_client
         .get_account(&vote_account_keypair.pubkey())
+        .await
         .unwrap();
     let vote_state =
         VoteStateV4::deserialize(vote_account.data(), &vote_account_keypair.pubkey()).unwrap();
@@ -132,7 +141,7 @@ fn test_vote_authorize_and_withdraw(compute_unit_price: Option<u64>) {
         vote_authorize: VoteAuthorize::Withdrawer,
         sign_only: false,
         dump_transaction_message: false,
-        blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
+        blockhash_query: BlockhashQuery::Rpc(Source::Cluster),
         nonce_account: None,
         nonce_authority: 0,
         memo: None,
@@ -141,7 +150,7 @@ fn test_vote_authorize_and_withdraw(compute_unit_price: Option<u64>) {
         new_authorized: Some(1),
         compute_unit_price,
     };
-    process_command(&config).unwrap_err(); // unsigned by new authority should fail
+    process_command(&config).await.unwrap_err(); // unsigned by new authority should fail
     config.signers = vec![
         &default_signer,
         &first_withdraw_authority,
@@ -153,7 +162,7 @@ fn test_vote_authorize_and_withdraw(compute_unit_price: Option<u64>) {
         vote_authorize: VoteAuthorize::Withdrawer,
         sign_only: false,
         dump_transaction_message: false,
-        blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
+        blockhash_query: BlockhashQuery::Rpc(Source::Cluster),
         nonce_account: None,
         nonce_authority: 0,
         memo: None,
@@ -162,9 +171,10 @@ fn test_vote_authorize_and_withdraw(compute_unit_price: Option<u64>) {
         new_authorized: Some(2),
         compute_unit_price,
     };
-    process_command(&config).unwrap();
+    process_command(&config).await.unwrap();
     let vote_account = rpc_client
         .get_account(&vote_account_keypair.pubkey())
+        .await
         .unwrap();
     let vote_state =
         VoteStateV4::deserialize(vote_account.data(), &vote_account_keypair.pubkey()).unwrap();
@@ -181,14 +191,14 @@ fn test_vote_authorize_and_withdraw(compute_unit_price: Option<u64>) {
         destination_account_pubkey: destination_account,
         sign_only: false,
         dump_transaction_message: false,
-        blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
+        blockhash_query: BlockhashQuery::Rpc(Source::Cluster),
         nonce_account: None,
         nonce_authority: 0,
         memo: None,
         fee_payer: 0,
         compute_unit_price,
     };
-    process_command(&config).unwrap();
+    process_command(&config).await.unwrap();
     let expected_balance = expected_balance - 1_000;
     check_balance!(expected_balance, &rpc_client, &vote_account_pubkey);
     check_balance!(1_000, &rpc_client, &destination_account);
@@ -202,14 +212,14 @@ fn test_vote_authorize_and_withdraw(compute_unit_price: Option<u64>) {
         withdraw_authority: 1,
         sign_only: false,
         dump_transaction_message: false,
-        blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
+        blockhash_query: BlockhashQuery::Rpc(Source::Cluster),
         nonce_account: None,
         nonce_authority: 0,
         memo: None,
         fee_payer: 0,
         compute_unit_price,
     };
-    process_command(&config).unwrap();
+    process_command(&config).await.unwrap();
 
     // Close vote account
     let destination_account = solana_pubkey::new_rand(); // Send withdrawal to new account to make balance check easy
@@ -222,19 +232,23 @@ fn test_vote_authorize_and_withdraw(compute_unit_price: Option<u64>) {
         fee_payer: 0,
         compute_unit_price,
     };
-    process_command(&config).unwrap();
+    process_command(&config).await.unwrap();
     check_balance!(0, &rpc_client, &vote_account_pubkey);
     check_balance!(expected_balance, &rpc_client, &destination_account);
 }
 
+#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
 #[test_case(None; "base")]
 #[test_case(Some(1_000_000); "with_compute_unit_price")]
-fn test_offline_vote_authorize_and_withdraw(compute_unit_price: Option<u64>) {
+async fn test_offline_vote_authorize_and_withdraw(compute_unit_price: Option<u64>) {
     let mint_keypair = Keypair::new();
-    let mint_pubkey = mint_keypair.pubkey();
-    let faucet_addr = run_local_faucet_with_unique_port_for_tests(mint_keypair);
-    let test_validator =
-        TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified);
+    let faucet_addr = run_local_faucet_with_unique_port_for_tests(mint_keypair.insecure_clone());
+    let test_validator = TestValidator::async_with_no_fees(
+        &mint_keypair,
+        Some(faucet_addr),
+        SocketAddrSpace::Unspecified,
+    )
+    .await;
 
     let rpc_client =
         RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
@@ -250,7 +264,7 @@ fn test_offline_vote_authorize_and_withdraw(compute_unit_price: Option<u64>) {
     let offline_keypair = Keypair::new();
     config_offline.signers = vec![&offline_keypair];
     // Verify that we cannot reach the cluster
-    process_command(&config_offline).unwrap_err();
+    process_command(&config_offline).await.unwrap_err();
 
     request_and_confirm_airdrop(
         &rpc_client,
@@ -258,6 +272,7 @@ fn test_offline_vote_authorize_and_withdraw(compute_unit_price: Option<u64>) {
         &config_payer.signers[0].pubkey(),
         100_000,
     )
+    .await
     .unwrap();
     check_balance!(100_000, &rpc_client, &config_payer.signers[0].pubkey());
 
@@ -267,6 +282,7 @@ fn test_offline_vote_authorize_and_withdraw(compute_unit_price: Option<u64>) {
         &config_offline.signers[0].pubkey(),
         100_000,
     )
+    .await
     .unwrap();
     check_balance!(100_000, &rpc_client, &config_offline.signers[0].pubkey());
 
@@ -283,16 +299,17 @@ fn test_offline_vote_authorize_and_withdraw(compute_unit_price: Option<u64>) {
         commission: 0,
         sign_only: false,
         dump_transaction_message: false,
-        blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
+        blockhash_query: BlockhashQuery::Rpc(Source::Cluster),
         nonce_account: None,
         nonce_authority: 0,
         memo: None,
         fee_payer: 0,
         compute_unit_price,
     };
-    process_command(&config_payer).unwrap();
+    process_command(&config_payer).await.unwrap();
     let vote_account = rpc_client
         .get_account(&vote_account_keypair.pubkey())
+        .await
         .unwrap();
     let vote_state =
         VoteStateV4::deserialize(vote_account.data(), &vote_account_keypair.pubkey()).unwrap();
@@ -300,6 +317,7 @@ fn test_offline_vote_authorize_and_withdraw(compute_unit_price: Option<u64>) {
     assert_eq!(authorized_withdrawer, offline_keypair.pubkey());
     let expected_balance = rpc_client
         .get_minimum_balance_for_rent_exemption(VoteStateV4::size_of())
+        .await
         .unwrap()
         .max(1);
     check_balance!(expected_balance, &rpc_client, &vote_account_pubkey);
@@ -314,7 +332,7 @@ fn test_offline_vote_authorize_and_withdraw(compute_unit_price: Option<u64>) {
         dump_transaction_message: false,
         allow_unfunded_recipient: true,
         no_wait: false,
-        blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
+        blockhash_query: BlockhashQuery::Rpc(Source::Cluster),
         nonce_account: None,
         nonce_authority: 0,
         memo: None,
@@ -323,20 +341,20 @@ fn test_offline_vote_authorize_and_withdraw(compute_unit_price: Option<u64>) {
         derived_address_program_id: None,
         compute_unit_price,
     };
-    process_command(&config_payer).unwrap();
+    process_command(&config_payer).await.unwrap();
     let expected_balance = expected_balance + 10_000;
     check_balance!(expected_balance, &rpc_client, &vote_account_pubkey);
 
     // Authorize vote account withdrawal to another signer, offline
     let withdraw_authority = Keypair::new();
-    let blockhash = rpc_client.get_latest_blockhash().unwrap();
+    let blockhash = rpc_client.get_latest_blockhash().await.unwrap();
     config_offline.command = CliCommand::VoteAuthorize {
         vote_account_pubkey,
         new_authorized_pubkey: withdraw_authority.pubkey(),
         vote_authorize: VoteAuthorize::Withdrawer,
         sign_only: true,
         dump_transaction_message: false,
-        blockhash_query: BlockhashQuery::None(blockhash),
+        blockhash_query: BlockhashQuery::Static(blockhash),
         nonce_account: None,
         nonce_authority: 0,
         memo: None,
@@ -346,7 +364,7 @@ fn test_offline_vote_authorize_and_withdraw(compute_unit_price: Option<u64>) {
         compute_unit_price,
     };
     config_offline.output_format = OutputFormat::JsonCompact;
-    let sig_response = process_command(&config_offline).unwrap();
+    let sig_response = process_command(&config_offline).await.unwrap();
     let sign_only = parse_sign_only_reply_string(&sig_response);
     assert!(sign_only.has_all_signers());
     let offline_presigner = sign_only
@@ -359,7 +377,7 @@ fn test_offline_vote_authorize_and_withdraw(compute_unit_price: Option<u64>) {
         vote_authorize: VoteAuthorize::Withdrawer,
         sign_only: false,
         dump_transaction_message: false,
-        blockhash_query: BlockhashQuery::FeeCalculator(blockhash_query::Source::Cluster, blockhash),
+        blockhash_query: BlockhashQuery::Validated(Source::Cluster, blockhash),
         nonce_account: None,
         nonce_authority: 0,
         memo: None,
@@ -368,9 +386,10 @@ fn test_offline_vote_authorize_and_withdraw(compute_unit_price: Option<u64>) {
         new_authorized: None,
         compute_unit_price,
     };
-    process_command(&config_payer).unwrap();
+    process_command(&config_payer).await.unwrap();
     let vote_account = rpc_client
         .get_account(&vote_account_keypair.pubkey())
+        .await
         .unwrap();
     let vote_state =
         VoteStateV4::deserialize(vote_account.data(), &vote_account_keypair.pubkey()).unwrap();
@@ -379,7 +398,7 @@ fn test_offline_vote_authorize_and_withdraw(compute_unit_price: Option<u64>) {
 
     // Withdraw from vote account offline
     let destination_account = solana_pubkey::new_rand(); // Send withdrawal to new account to make balance check easy
-    let blockhash = rpc_client.get_latest_blockhash().unwrap();
+    let blockhash = rpc_client.get_latest_blockhash().await.unwrap();
     let fee_payer_null_signer = NullSigner::new(&default_signer.pubkey());
     config_offline.signers = vec![&fee_payer_null_signer, &withdraw_authority];
     config_offline.command = CliCommand::WithdrawFromVoteAccount {
@@ -389,7 +408,7 @@ fn test_offline_vote_authorize_and_withdraw(compute_unit_price: Option<u64>) {
         destination_account_pubkey: destination_account,
         sign_only: true,
         dump_transaction_message: false,
-        blockhash_query: BlockhashQuery::None(blockhash),
+        blockhash_query: BlockhashQuery::Static(blockhash),
         nonce_account: None,
         nonce_authority: 0,
         memo: None,
@@ -397,7 +416,7 @@ fn test_offline_vote_authorize_and_withdraw(compute_unit_price: Option<u64>) {
         compute_unit_price,
     };
     config_offline.output_format = OutputFormat::JsonCompact;
-    let sig_response = process_command(&config_offline).unwrap();
+    let sig_response = process_command(&config_offline).await.unwrap();
     let sign_only = parse_sign_only_reply_string(&sig_response);
     let offline_presigner = sign_only
         .presigner_of(&config_offline.signers[1].pubkey())
@@ -410,20 +429,20 @@ fn test_offline_vote_authorize_and_withdraw(compute_unit_price: Option<u64>) {
         destination_account_pubkey: destination_account,
         sign_only: false,
         dump_transaction_message: false,
-        blockhash_query: BlockhashQuery::FeeCalculator(blockhash_query::Source::Cluster, blockhash),
+        blockhash_query: BlockhashQuery::Validated(Source::Cluster, blockhash),
         nonce_account: None,
         nonce_authority: 0,
         memo: None,
         fee_payer: 0,
         compute_unit_price,
     };
-    process_command(&config_payer).unwrap();
+    process_command(&config_payer).await.unwrap();
     let expected_balance = expected_balance - 1_000;
     check_balance!(expected_balance, &rpc_client, &vote_account_pubkey);
     check_balance!(1_000, &rpc_client, &destination_account);
 
     // Re-assign validator identity offline
-    let blockhash = rpc_client.get_latest_blockhash().unwrap();
+    let blockhash = rpc_client.get_latest_blockhash().await.unwrap();
     let new_identity_keypair = Keypair::new();
     let new_identity_null_signer = NullSigner::new(&new_identity_keypair.pubkey());
     config_offline.signers = vec![
@@ -437,16 +456,16 @@ fn test_offline_vote_authorize_and_withdraw(compute_unit_price: Option<u64>) {
         withdraw_authority: 1,
         sign_only: true,
         dump_transaction_message: false,
-        blockhash_query: BlockhashQuery::None(blockhash),
+        blockhash_query: BlockhashQuery::Static(blockhash),
         nonce_account: None,
         nonce_authority: 0,
         memo: None,
         fee_payer: 0,
         compute_unit_price,
     };
-    process_command(&config_offline).unwrap();
+    process_command(&config_offline).await.unwrap();
     config_offline.output_format = OutputFormat::JsonCompact;
-    let sig_response = process_command(&config_offline).unwrap();
+    let sig_response = process_command(&config_offline).await.unwrap();
     let sign_only = parse_sign_only_reply_string(&sig_response);
     let offline_presigner = sign_only
         .presigner_of(&config_offline.signers[1].pubkey())
@@ -458,14 +477,14 @@ fn test_offline_vote_authorize_and_withdraw(compute_unit_price: Option<u64>) {
         withdraw_authority: 1,
         sign_only: false,
         dump_transaction_message: false,
-        blockhash_query: BlockhashQuery::FeeCalculator(blockhash_query::Source::Cluster, blockhash),
+        blockhash_query: BlockhashQuery::Validated(Source::Cluster, blockhash),
         nonce_account: None,
         nonce_authority: 0,
         memo: None,
         fee_payer: 0,
         compute_unit_price,
     };
-    process_command(&config_payer).unwrap();
+    process_command(&config_payer).await.unwrap();
 
     // Close vote account offline. Must use WithdrawFromVoteAccount and specify amount, since
     // CloseVoteAccount requires RpcClient
@@ -478,16 +497,16 @@ fn test_offline_vote_authorize_and_withdraw(compute_unit_price: Option<u64>) {
         destination_account_pubkey: destination_account,
         sign_only: true,
         dump_transaction_message: false,
-        blockhash_query: BlockhashQuery::None(blockhash),
+        blockhash_query: BlockhashQuery::Static(blockhash),
         nonce_account: None,
         nonce_authority: 0,
         memo: None,
         fee_payer: 0,
         compute_unit_price,
     };
-    process_command(&config_offline).unwrap();
+    process_command(&config_offline).await.unwrap();
     config_offline.output_format = OutputFormat::JsonCompact;
-    let sig_response = process_command(&config_offline).unwrap();
+    let sig_response = process_command(&config_offline).await.unwrap();
     let sign_only = parse_sign_only_reply_string(&sig_response);
     let offline_presigner = sign_only
         .presigner_of(&config_offline.signers[1].pubkey())
@@ -500,14 +519,14 @@ fn test_offline_vote_authorize_and_withdraw(compute_unit_price: Option<u64>) {
         destination_account_pubkey: destination_account,
         sign_only: false,
         dump_transaction_message: false,
-        blockhash_query: BlockhashQuery::FeeCalculator(blockhash_query::Source::Cluster, blockhash),
+        blockhash_query: BlockhashQuery::Validated(Source::Cluster, blockhash),
         nonce_account: None,
         nonce_authority: 0,
         memo: None,
         fee_payer: 0,
         compute_unit_price,
     };
-    process_command(&config_payer).unwrap();
+    process_command(&config_payer).await.unwrap();
     check_balance!(0, &rpc_client, &vote_account_pubkey);
     check_balance!(expected_balance, &rpc_client, &destination_account);
 }

+ 192 - 56
test-validator/src/lib.rs

@@ -52,7 +52,9 @@ use {
     solana_rent::Rent,
     solana_rpc::{rpc::JsonRpcConfig, rpc_pubsub_service::PubSubConfig},
     solana_rpc_client::{nonblocking, rpc_client::RpcClient},
-    solana_rpc_client_api::request::MAX_MULTIPLE_ACCOUNTS,
+    solana_rpc_client_api::{
+        client_error::Error as RpcClientError, request::MAX_MULTIPLE_ACCOUNTS,
+    },
     solana_runtime::{
         bank_forks::BankForks,
         genesis_utils::{self, create_genesis_config_with_leader_ex_no_features},
@@ -62,7 +64,7 @@ use {
     solana_signer::Signer,
     solana_streamer::quic::DEFAULT_QUIC_ENDPOINTS,
     solana_tpu_client::tpu_client::DEFAULT_TPU_ENABLE_UDP,
-    solana_transaction::Transaction,
+    solana_transaction::{Transaction, TransactionError},
     solana_validator_exit::Exit,
     std::{
         collections::{HashMap, HashSet},
@@ -672,6 +674,7 @@ impl TestValidatorGenesis {
     /// Start a test validator with the address of the mint account that will receive tokens
     /// created at genesis.
     ///
+    /// Sync only; calling from a tokio runtime will panic due to nested runtimes.
     pub fn start_with_mint_address(
         &self,
         mint_address: Pubkey,
@@ -684,6 +687,7 @@ impl TestValidatorGenesis {
     /// created at genesis. Augments admin rpc service with dynamic geyser plugin manager if
     /// the geyser plugin service is enabled at startup.
     ///
+    /// Sync only; calling from a tokio runtime will panic due to nested runtimes.
     pub fn start_with_mint_address_and_geyser_plugin_rpc(
         &self,
         mint_address: Pubkey,
@@ -722,6 +726,7 @@ impl TestValidatorGenesis {
     /// created at genesis.
     ///
     /// This function panics on initialization failure.
+    /// Sync only; calling from a tokio runtime will panic due to nested runtimes.
     pub fn start_with_socket_addr_space(
         &self,
         socket_addr_space: SocketAddrSpace,
@@ -739,15 +744,41 @@ impl TestValidatorGenesis {
                     .iter()
                     .map(|p| &p.program_id)
                     .collect();
-                runtime.block_on(test_validator.wait_for_upgradeable_programs_deployed(
-                    &upgradeable_program_ids,
-                    &mint_keypair,
-                ));
+                runtime
+                    .block_on(test_validator.wait_for_upgradeable_programs_deployed(
+                        &upgradeable_program_ids,
+                        &mint_keypair,
+                    ))
+                    .unwrap_or_else(|err| {
+                        panic!("Failed to wait for programs to be deployed: {err:?}")
+                    });
             })
             .map(|test_validator| (test_validator, mint_keypair))
             .unwrap_or_else(|err| panic!("Test validator failed to start: {err}"))
     }
 
+    /// Start a test validator with the address of the mint account that will receive tokens
+    /// created at genesis (async version).
+    pub async fn start_async_with_mint_address(
+        &self,
+        mint_keypair: &Keypair,
+        socket_addr_space: SocketAddrSpace,
+    ) -> Result<TestValidator, Box<dyn std::error::Error>> {
+        let test_validator =
+            TestValidator::start(mint_keypair.pubkey(), self, socket_addr_space, None)?;
+        test_validator.wait_for_nonzero_fees().await;
+        let upgradeable_program_ids: Vec<&Pubkey> = self
+            .upgradeable_programs
+            .iter()
+            .map(|p| &p.program_id)
+            .collect();
+        test_validator
+            .wait_for_upgradeable_programs_deployed(&upgradeable_program_ids, mint_keypair)
+            .await
+            .unwrap_or_else(|err| panic!("Failed to wait for programs to be deployed: {err:?}"));
+        Ok(test_validator)
+    }
+
     pub async fn start_async(&self) -> (TestValidator, Keypair) {
         self.start_async_with_socket_addr_space(SocketAddrSpace::new(
             /*allow_private_addr=*/ true,
@@ -760,21 +791,11 @@ impl TestValidatorGenesis {
         socket_addr_space: SocketAddrSpace,
     ) -> (TestValidator, Keypair) {
         let mint_keypair = Keypair::new();
-        match TestValidator::start(mint_keypair.pubkey(), self, socket_addr_space, None) {
-            Ok(test_validator) => {
-                test_validator.wait_for_nonzero_fees().await;
-                let upgradeable_program_ids: Vec<&Pubkey> = self
-                    .upgradeable_programs
-                    .iter()
-                    .map(|p| &p.program_id)
-                    .collect();
-                test_validator
-                    .wait_for_upgradeable_programs_deployed(&upgradeable_program_ids, &mint_keypair)
-                    .await;
-                (test_validator, mint_keypair)
-            }
-            Err(err) => panic!("Test validator failed to start: {err}"),
-        }
+        let test_validator = self
+            .start_async_with_mint_address(&mint_keypair, socket_addr_space)
+            .await
+            .unwrap_or_else(|err| panic!("Test validator failed to start: {err}"));
+        (test_validator, mint_keypair)
     }
 }
 
@@ -790,17 +811,19 @@ pub struct TestValidator {
 }
 
 impl TestValidator {
-    /// Create and start a `TestValidator` with no transaction fees and minimal rent.
-    /// Faucet optional.
-    ///
-    /// This function panics on initialization failure.
-    pub fn with_no_fees(
+    /// Create a configured genesis and start validator
+    /// Sync only; calling from a tokio runtime will panic due to nested runtimes.
+    fn start_with_config(
         mint_address: Pubkey,
         faucet_addr: Option<SocketAddr>,
         socket_addr_space: SocketAddrSpace,
+        target_lamports_per_signature: u64,
+        tpu_enable_udp: bool,
+        wait_for_fees: bool,
     ) -> Self {
-        TestValidatorGenesis::default()
-            .fee_rate_governor(FeeRateGovernor::new(0, 0))
+        let test_validator = TestValidatorGenesis::default()
+            .tpu_enable_udp(tpu_enable_udp)
+            .fee_rate_governor(FeeRateGovernor::new(target_lamports_per_signature, 0))
             .rent(Rent {
                 lamports_per_byte_year: 1,
                 exemption_threshold: 1.0,
@@ -808,28 +831,67 @@ impl TestValidator {
             })
             .faucet_addr(faucet_addr)
             .start_with_mint_address(mint_address, socket_addr_space)
-            .expect("validator start failed")
+            .expect("validator start failed");
+
+        if wait_for_fees {
+            let runtime = tokio::runtime::Builder::new_current_thread()
+                .enable_io()
+                .enable_time()
+                .build()
+                .unwrap();
+            runtime.block_on(test_validator.wait_for_nonzero_fees());
+        }
+        test_validator
     }
 
-    /// Create a test validator using udp for TPU.
-    pub fn with_no_fees_udp(
-        mint_address: Pubkey,
+    /// Create a configured genesis and start validator (async version)
+    async fn async_start_with_config(
+        mint_keypair: &Keypair,
         faucet_addr: Option<SocketAddr>,
         socket_addr_space: SocketAddrSpace,
+        target_lamports_per_signature: u64,
     ) -> Self {
         TestValidatorGenesis::default()
-            .tpu_enable_udp(true)
-            .fee_rate_governor(FeeRateGovernor::new(0, 0))
+            .fee_rate_governor(FeeRateGovernor::new(target_lamports_per_signature, 0))
             .rent(Rent {
                 lamports_per_byte_year: 1,
                 exemption_threshold: 1.0,
                 ..Rent::default()
             })
             .faucet_addr(faucet_addr)
-            .start_with_mint_address(mint_address, socket_addr_space)
+            .start_async_with_mint_address(mint_keypair, socket_addr_space)
+            .await
             .expect("validator start failed")
     }
 
+    /// Create and start a `TestValidator` with no transaction fees and minimal rent.
+    /// Faucet optional.
+    ///
+    /// This function panics on initialization failure.
+    pub fn with_no_fees(
+        mint_address: Pubkey,
+        faucet_addr: Option<SocketAddr>,
+        socket_addr_space: SocketAddrSpace,
+    ) -> Self {
+        Self::start_with_config(
+            mint_address,
+            faucet_addr,
+            socket_addr_space,
+            0,
+            false,
+            false,
+        )
+    }
+
+    /// Create a test validator using udp for TPU.
+    pub fn with_no_fees_udp(
+        mint_address: Pubkey,
+        faucet_addr: Option<SocketAddr>,
+        socket_addr_space: SocketAddrSpace,
+    ) -> Self {
+        Self::start_with_config(mint_address, faucet_addr, socket_addr_space, 0, true, false)
+    }
+
     /// Create and start a `TestValidator` with custom transaction fees and minimal rent.
     /// Faucet optional.
     ///
@@ -840,16 +902,45 @@ impl TestValidator {
         faucet_addr: Option<SocketAddr>,
         socket_addr_space: SocketAddrSpace,
     ) -> Self {
-        TestValidatorGenesis::default()
-            .fee_rate_governor(FeeRateGovernor::new(target_lamports_per_signature, 0))
-            .rent(Rent {
-                lamports_per_byte_year: 1,
-                exemption_threshold: 1.0,
-                ..Rent::default()
-            })
-            .faucet_addr(faucet_addr)
-            .start_with_mint_address(mint_address, socket_addr_space)
-            .expect("validator start failed")
+        Self::start_with_config(
+            mint_address,
+            faucet_addr,
+            socket_addr_space,
+            target_lamports_per_signature,
+            false,
+            true,
+        )
+    }
+
+    /// Create and start a `TestValidator` with no transaction fees and minimal rent (async version).
+    /// Faucet optional.
+    ///
+    /// This function panics on initialization failure.
+    pub async fn async_with_no_fees(
+        mint_keypair: &Keypair,
+        faucet_addr: Option<SocketAddr>,
+        socket_addr_space: SocketAddrSpace,
+    ) -> Self {
+        Self::async_start_with_config(mint_keypair, faucet_addr, socket_addr_space, 0).await
+    }
+
+    /// Create and start a `TestValidator` with custom transaction fees and minimal rent (async version).
+    /// Faucet optional.
+    ///
+    /// This function panics on initialization failure.
+    pub async fn async_with_custom_fees(
+        mint_keypair: &Keypair,
+        target_lamports_per_signature: u64,
+        faucet_addr: Option<SocketAddr>,
+        socket_addr_space: SocketAddrSpace,
+    ) -> Self {
+        Self::async_start_with_config(
+            mint_keypair,
+            faucet_addr,
+            socket_addr_space,
+            target_lamports_per_signature,
+        )
+        .await
     }
 
     /// Initialize the ledger directory
@@ -1225,12 +1316,15 @@ impl TestValidator {
     }
 
     /// programs added to genesis ain't immediately usable. Actively check "Program
-    /// is not deployed" error for their availability.
+    /// is not deployed" error for their availibility.
+    ///
+    /// Returns `TransactionError::AccountNotFound` if the payer account is not funded.
+    /// The caller is responsible for ensuring the payer account has sufficient funds.
     async fn wait_for_upgradeable_programs_deployed(
         &self,
         upgradeable_programs: &[&Pubkey],
         payer: &Keypair,
-    ) {
+    ) -> Result<(), RpcClientError> {
         let rpc_client = nonblocking::rpc_client::RpcClient::new_with_commitment(
             self.rpc_url.clone(),
             CommitmentConfig::processed(),
@@ -1256,22 +1350,38 @@ impl TestValidator {
                     &[&payer],
                     blockhash,
                 );
-                match rpc_client.send_transaction(&transaction).await {
-                    Ok(_) => *is_deployed = true,
-                    Err(e) => {
-                        if format!("{e:?}").contains("Program is not deployed") {
-                            debug!("{program_id:?} - not deployed");
+                match rpc_client.simulate_transaction(&transaction).await {
+                    Ok(response) => {
+                        if let Some(e) = response.value.err {
+                            let err_string = format!("{e:?}");
+                            if err_string.contains("Program is not deployed") {
+                                debug!("{program_id:?} - not deployed");
+                            } else if err_string.contains("AccountNotFound") {
+                                // Payer account not funded - this is a caller error
+                                return Err(RpcClientError::from(
+                                    TransactionError::AccountNotFound,
+                                ));
+                            } else {
+                                // Assuming all other errors could only occur *after*
+                                // program is deployed for usability
+                                *is_deployed = true;
+                                debug!("{program_id:?} - Unexpected error: {e:?}");
+                            }
                         } else {
-                            // Assuming all other other errors could only occur *after*
-                            // program is deployed for usability.
                             *is_deployed = true;
-                            debug!("{program_id:?} - Unexpected error: {e:?}");
+                        }
+                    }
+                    Err(e) => {
+                        warn!("Failed to simulate transaction: {e:?}");
+                        // Error if we're at final attempt - flakiness is tolerated up to MAX_ATTEMPTS
+                        if attempt == MAX_ATTEMPTS {
+                            return Err(e);
                         }
                     }
                 }
             }
             if deployed.iter().all(|&deployed| deployed) {
-                return;
+                return Ok(());
             }
 
             println!("Waiting for programs to be fully deployed {attempt} ...");
@@ -1550,4 +1660,30 @@ mod test {
         assert_eq!(account.owner, solana_sdk_ids::bpf_loader_upgradeable::id());
         assert!(account.executable);
     }
+
+    #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
+    async fn test_wait_for_program_with_unfunded_payer() {
+        let program_id = Pubkey::new_unique();
+        let (test_validator, _mint_keypair) = TestValidatorGenesis::default()
+            .add_program("../programs/bpf-loader-tests/noop", program_id)
+            .start_async()
+            .await;
+
+        // Create an unfunded payer keypair
+        let unfunded_payer = Keypair::new();
+
+        // Call wait_for_upgradeable_programs_deployed with unfunded payer
+        let result = test_validator
+            .wait_for_upgradeable_programs_deployed(&[&program_id], &unfunded_payer)
+            .await;
+
+        // Verify it returns AccountNotFound error
+        let err = result.unwrap_err();
+        assert!(matches!(
+            *err.kind,
+            solana_rpc_client_api::client_error::ErrorKind::TransactionError(
+                TransactionError::AccountNotFound
+            )
+        ));
+    }
 }

+ 1 - 0
transaction-dos/Cargo.toml

@@ -42,6 +42,7 @@ solana-system-interface = { workspace = true }
 solana-transaction = { workspace = true }
 solana-transaction-status = { workspace = true }
 solana-version = { workspace = true }
+tokio = { workspace = true }
 
 [dev-dependencies]
 solana-core = { workspace = true, features = ["dev-context-only-utils"] }

+ 10 - 7
transaction-dos/src/main.rs

@@ -138,7 +138,7 @@ fn make_dos_message(
 /// so they can't be parallelized
 ///
 #[allow(clippy::too_many_arguments)]
-fn run_transactions_dos(
+async fn run_transactions_dos(
     entrypoint_addr: SocketAddr,
     faucet_addr: SocketAddr,
     payer_keypairs: &[&Keypair],
@@ -254,7 +254,7 @@ fn run_transactions_dos(
             skip_feature_verification: true,
         });
 
-        process_command(&config).expect("deploy didn't pass");
+        process_command(&config).await.expect("deploy didn't pass");
     } else {
         info!("Found program account. Skipping deploy..");
         assert!(program_account.unwrap().executable);
@@ -426,7 +426,8 @@ fn run_transactions_dos(
     executor.close();
 }
 
-fn main() {
+#[tokio::main]
+async fn main() {
     agave_logger::setup_with_default_filter();
     let matches = App::new(crate_name!())
         .about(crate_description!())
@@ -660,7 +661,8 @@ fn main() {
         account_groups,
         just_calculate_fees,
         batch_sleep_ms,
-    );
+    )
+    .await;
 }
 
 #[cfg(test)]
@@ -703,9 +705,9 @@ pub mod test {
         assert!(size < PACKET_DATA_SIZE as u64);
     }
 
-    #[test]
+    #[tokio::test(flavor = "multi_thread")]
     #[ignore]
-    fn test_transaction_dos() {
+    async fn test_transaction_dos() {
         agave_logger::setup();
 
         let validator_config = ValidatorConfig::default_for_test();
@@ -761,7 +763,8 @@ pub mod test {
             maybe_account_groups,
             false,
             100,
-        );
+        )
+        .await;
         start.stop();
         info!("{start}");
     }

برخی فایل ها در این مقایسه diff نمایش داده نمی شوند زیرا تعداد فایل ها بسیار زیاد است