Use timeout to allow RpcClient to retry initial transaction confirmation (#18311)
* Tidying: relocate function * Use proper helper method for RpcClient commitment * Add RpcClientConfig * Add configurable confirm_transaction_initial_timeout * Use default 5s timeout for initial tx confirmation
This commit is contained in:
parent
ce53b84cdc
commit
9d4428d3d8
|
@ -63,6 +63,7 @@ use std::{
|
|||
use thiserror::Error;
|
||||
|
||||
pub const DEFAULT_RPC_TIMEOUT_SECONDS: &str = "30";
|
||||
pub const DEFAULT_CONFIRM_TX_TIMEOUT_SECONDS: &str = "5";
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
|
@ -453,6 +454,7 @@ pub struct CliConfig<'a> {
|
|||
pub output_format: OutputFormat,
|
||||
pub commitment: CommitmentConfig,
|
||||
pub send_transaction_config: RpcSendTransactionConfig,
|
||||
pub confirm_transaction_initial_timeout: Duration,
|
||||
pub address_labels: HashMap<String, String>,
|
||||
}
|
||||
|
||||
|
@ -597,6 +599,9 @@ impl Default for CliConfig<'_> {
|
|||
output_format: OutputFormat::Display,
|
||||
commitment: CommitmentConfig::confirmed(),
|
||||
send_transaction_config: RpcSendTransactionConfig::default(),
|
||||
confirm_transaction_initial_timeout: Duration::from_secs(
|
||||
u64::from_str(DEFAULT_CONFIRM_TX_TIMEOUT_SECONDS).unwrap(),
|
||||
),
|
||||
address_labels: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
@ -1288,10 +1293,11 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
|
|||
}
|
||||
|
||||
let rpc_client = if config.rpc_client.is_none() {
|
||||
Arc::new(RpcClient::new_with_timeout_and_commitment(
|
||||
Arc::new(RpcClient::new_with_timeouts_and_commitment(
|
||||
config.json_rpc_url.to_string(),
|
||||
config.rpc_timeout,
|
||||
config.commitment,
|
||||
config.confirm_transaction_initial_timeout,
|
||||
))
|
||||
} else {
|
||||
// Primarily for testing
|
||||
|
|
|
@ -10,7 +10,7 @@ use solana_clap_utils::{
|
|||
};
|
||||
use solana_cli::cli::{
|
||||
app, parse_command, process_command, CliCommandInfo, CliConfig, SettingType,
|
||||
DEFAULT_RPC_TIMEOUT_SECONDS,
|
||||
DEFAULT_CONFIRM_TX_TIMEOUT_SECONDS, DEFAULT_RPC_TIMEOUT_SECONDS,
|
||||
};
|
||||
use solana_cli_config::{Config, CONFIG_FILE};
|
||||
use solana_cli_output::{display::println_name_value, OutputFormat};
|
||||
|
@ -167,6 +167,11 @@ pub fn parse_args<'a>(
|
|||
let rpc_timeout = value_t_or_exit!(matches, "rpc_timeout", u64);
|
||||
let rpc_timeout = Duration::from_secs(rpc_timeout);
|
||||
|
||||
let confirm_transaction_initial_timeout =
|
||||
value_t_or_exit!(matches, "confirm_transaction_initial_timeout", u64);
|
||||
let confirm_transaction_initial_timeout =
|
||||
Duration::from_secs(confirm_transaction_initial_timeout);
|
||||
|
||||
let (_, websocket_url) = CliConfig::compute_websocket_url_setting(
|
||||
matches.value_of("websocket_url").unwrap_or(""),
|
||||
&config.websocket_url,
|
||||
|
@ -235,6 +240,7 @@ pub fn parse_args<'a>(
|
|||
preflight_commitment: Some(commitment.commitment),
|
||||
..RpcSendTransactionConfig::default()
|
||||
},
|
||||
confirm_transaction_initial_timeout,
|
||||
address_labels,
|
||||
},
|
||||
signers,
|
||||
|
@ -350,6 +356,16 @@ fn main() -> Result<(), Box<dyn error::Error>> {
|
|||
.hidden(true)
|
||||
.help("Timeout value for RPC requests"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("confirm_transaction_initial_timeout")
|
||||
.long("confirm-timeout")
|
||||
.value_name("SECONDS")
|
||||
.takes_value(true)
|
||||
.default_value(DEFAULT_CONFIRM_TX_TIMEOUT_SECONDS)
|
||||
.global(true)
|
||||
.hidden(true)
|
||||
.help("Timeout value for initial transaction status"),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("config")
|
||||
.about("Solana command-line tool configuration settings")
|
||||
|
|
|
@ -49,41 +49,36 @@ use {
|
|||
},
|
||||
};
|
||||
|
||||
pub struct RpcClient {
|
||||
sender: Box<dyn RpcSender + Send + Sync + 'static>,
|
||||
#[derive(Default)]
|
||||
pub struct RpcClientConfig {
|
||||
commitment_config: CommitmentConfig,
|
||||
node_version: RwLock<Option<semver::Version>>,
|
||||
confirm_transaction_initial_timeout: Option<Duration>,
|
||||
}
|
||||
|
||||
fn serialize_encode_transaction(
|
||||
transaction: &Transaction,
|
||||
encoding: UiTransactionEncoding,
|
||||
) -> ClientResult<String> {
|
||||
let serialized = serialize(transaction)
|
||||
.map_err(|e| ClientErrorKind::Custom(format!("transaction serialization failed: {}", e)))?;
|
||||
let encoded = match encoding {
|
||||
UiTransactionEncoding::Base58 => bs58::encode(serialized).into_string(),
|
||||
UiTransactionEncoding::Base64 => base64::encode(serialized),
|
||||
_ => {
|
||||
return Err(ClientErrorKind::Custom(format!(
|
||||
"unsupported transaction encoding: {}. Supported encodings: base58, base64",
|
||||
encoding
|
||||
))
|
||||
.into())
|
||||
impl RpcClientConfig {
|
||||
fn with_commitment(commitment_config: CommitmentConfig) -> Self {
|
||||
RpcClientConfig {
|
||||
commitment_config,
|
||||
..Self::default()
|
||||
}
|
||||
};
|
||||
Ok(encoded)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RpcClient {
|
||||
sender: Box<dyn RpcSender + Send + Sync + 'static>,
|
||||
config: RpcClientConfig,
|
||||
node_version: RwLock<Option<semver::Version>>,
|
||||
}
|
||||
|
||||
impl RpcClient {
|
||||
fn new_sender<T: RpcSender + Send + Sync + 'static>(
|
||||
sender: T,
|
||||
commitment_config: CommitmentConfig,
|
||||
config: RpcClientConfig,
|
||||
) -> Self {
|
||||
Self {
|
||||
sender: Box::new(sender),
|
||||
node_version: RwLock::new(None),
|
||||
commitment_config,
|
||||
config,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -92,13 +87,16 @@ impl RpcClient {
|
|||
}
|
||||
|
||||
pub fn new_with_commitment(url: String, commitment_config: CommitmentConfig) -> Self {
|
||||
Self::new_sender(HttpSender::new(url), commitment_config)
|
||||
Self::new_sender(
|
||||
HttpSender::new(url),
|
||||
RpcClientConfig::with_commitment(commitment_config),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn new_with_timeout(url: String, timeout: Duration) -> Self {
|
||||
Self::new_sender(
|
||||
HttpSender::new_with_timeout(url, timeout),
|
||||
CommitmentConfig::default(),
|
||||
RpcClientConfig::with_commitment(CommitmentConfig::default()),
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -109,18 +107,36 @@ impl RpcClient {
|
|||
) -> Self {
|
||||
Self::new_sender(
|
||||
HttpSender::new_with_timeout(url, timeout),
|
||||
commitment_config,
|
||||
RpcClientConfig::with_commitment(commitment_config),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn new_with_timeouts_and_commitment(
|
||||
url: String,
|
||||
timeout: Duration,
|
||||
commitment_config: CommitmentConfig,
|
||||
confirm_transaction_initial_timeout: Duration,
|
||||
) -> Self {
|
||||
Self::new_sender(
|
||||
HttpSender::new_with_timeout(url, timeout),
|
||||
RpcClientConfig {
|
||||
commitment_config,
|
||||
confirm_transaction_initial_timeout: Some(confirm_transaction_initial_timeout),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub fn new_mock(url: String) -> Self {
|
||||
Self::new_sender(MockSender::new(url), CommitmentConfig::default())
|
||||
Self::new_sender(
|
||||
MockSender::new(url),
|
||||
RpcClientConfig::with_commitment(CommitmentConfig::default()),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn new_mock_with_mocks(url: String, mocks: Mocks) -> Self {
|
||||
Self::new_sender(
|
||||
MockSender::new_with_mocks(url, mocks),
|
||||
CommitmentConfig::default(),
|
||||
RpcClientConfig::with_commitment(CommitmentConfig::default()),
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -159,7 +175,7 @@ impl RpcClient {
|
|||
}
|
||||
|
||||
pub fn commitment(&self) -> CommitmentConfig {
|
||||
self.commitment_config
|
||||
self.config.commitment_config
|
||||
}
|
||||
|
||||
fn use_deprecated_commitment(&self) -> Result<bool, RpcError> {
|
||||
|
@ -201,7 +217,7 @@ impl RpcClient {
|
|||
|
||||
pub fn confirm_transaction(&self, signature: &Signature) -> ClientResult<bool> {
|
||||
Ok(self
|
||||
.confirm_transaction_with_commitment(signature, self.commitment_config)?
|
||||
.confirm_transaction_with_commitment(signature, self.commitment())?
|
||||
.value)
|
||||
}
|
||||
|
||||
|
@ -227,8 +243,7 @@ impl RpcClient {
|
|||
transaction,
|
||||
RpcSendTransactionConfig {
|
||||
preflight_commitment: Some(
|
||||
self.maybe_map_commitment(self.commitment_config)?
|
||||
.commitment,
|
||||
self.maybe_map_commitment(self.commitment())?.commitment,
|
||||
),
|
||||
..RpcSendTransactionConfig::default()
|
||||
},
|
||||
|
@ -317,7 +332,7 @@ impl RpcClient {
|
|||
self.simulate_transaction_with_config(
|
||||
transaction,
|
||||
RpcSimulateTransactionConfig {
|
||||
commitment: Some(self.commitment_config),
|
||||
commitment: Some(self.commitment()),
|
||||
..RpcSimulateTransactionConfig::default()
|
||||
},
|
||||
)
|
||||
|
@ -355,7 +370,7 @@ impl RpcClient {
|
|||
&self,
|
||||
signature: &Signature,
|
||||
) -> ClientResult<Option<transaction::Result<()>>> {
|
||||
self.get_signature_status_with_commitment(signature, self.commitment_config)
|
||||
self.get_signature_status_with_commitment(signature, self.commitment())
|
||||
}
|
||||
|
||||
pub fn get_signature_statuses(
|
||||
|
@ -413,7 +428,7 @@ impl RpcClient {
|
|||
}
|
||||
|
||||
pub fn get_slot(&self) -> ClientResult<Slot> {
|
||||
self.get_slot_with_commitment(self.commitment_config)
|
||||
self.get_slot_with_commitment(self.commitment())
|
||||
}
|
||||
|
||||
pub fn get_slot_with_commitment(
|
||||
|
@ -427,7 +442,7 @@ impl RpcClient {
|
|||
}
|
||||
|
||||
pub fn get_block_height(&self) -> ClientResult<u64> {
|
||||
self.get_block_height_with_commitment(self.commitment_config)
|
||||
self.get_block_height_with_commitment(self.commitment())
|
||||
}
|
||||
|
||||
pub fn get_block_height_with_commitment(
|
||||
|
@ -481,14 +496,14 @@ impl RpcClient {
|
|||
stake_account.to_string(),
|
||||
RpcEpochConfig {
|
||||
epoch,
|
||||
commitment: Some(self.commitment_config),
|
||||
commitment: Some(self.commitment()),
|
||||
}
|
||||
]),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn supply(&self) -> RpcResult<RpcSupply> {
|
||||
self.supply_with_commitment(self.commitment_config)
|
||||
self.supply_with_commitment(self.commitment())
|
||||
}
|
||||
|
||||
pub fn supply_with_commitment(
|
||||
|
@ -515,7 +530,7 @@ impl RpcClient {
|
|||
}
|
||||
|
||||
pub fn get_vote_accounts(&self) -> ClientResult<RpcVoteAccountStatus> {
|
||||
self.get_vote_accounts_with_commitment(self.commitment_config)
|
||||
self.get_vote_accounts_with_commitment(self.commitment())
|
||||
}
|
||||
|
||||
pub fn get_vote_accounts_with_commitment(
|
||||
|
@ -892,7 +907,7 @@ impl RpcClient {
|
|||
}
|
||||
|
||||
pub fn get_epoch_info(&self) -> ClientResult<EpochInfo> {
|
||||
self.get_epoch_info_with_commitment(self.commitment_config)
|
||||
self.get_epoch_info_with_commitment(self.commitment())
|
||||
}
|
||||
|
||||
pub fn get_epoch_info_with_commitment(
|
||||
|
@ -909,7 +924,7 @@ impl RpcClient {
|
|||
&self,
|
||||
slot: Option<Slot>,
|
||||
) -> ClientResult<Option<RpcLeaderSchedule>> {
|
||||
self.get_leader_schedule_with_commitment(slot, self.commitment_config)
|
||||
self.get_leader_schedule_with_commitment(slot, self.commitment())
|
||||
}
|
||||
|
||||
pub fn get_leader_schedule_with_commitment(
|
||||
|
@ -979,7 +994,7 @@ impl RpcClient {
|
|||
addresses,
|
||||
RpcEpochConfig {
|
||||
epoch,
|
||||
commitment: Some(self.commitment_config),
|
||||
commitment: Some(self.commitment()),
|
||||
}
|
||||
]),
|
||||
)
|
||||
|
@ -1045,7 +1060,7 @@ impl RpcClient {
|
|||
/// Note that `get_account` returns `Err(..)` if the account does not exist whereas
|
||||
/// `get_account_with_commitment` returns `Ok(None)` if the account does not exist.
|
||||
pub fn get_account(&self, pubkey: &Pubkey) -> ClientResult<Account> {
|
||||
self.get_account_with_commitment(pubkey, self.commitment_config)?
|
||||
self.get_account_with_commitment(pubkey, self.commitment())?
|
||||
.value
|
||||
.ok_or_else(|| RpcError::ForUser(format!("AccountNotFound: pubkey={}", pubkey)).into())
|
||||
}
|
||||
|
@ -1101,7 +1116,7 @@ impl RpcClient {
|
|||
|
||||
pub fn get_multiple_accounts(&self, pubkeys: &[Pubkey]) -> ClientResult<Vec<Option<Account>>> {
|
||||
Ok(self
|
||||
.get_multiple_accounts_with_commitment(pubkeys, self.commitment_config)?
|
||||
.get_multiple_accounts_with_commitment(pubkeys, self.commitment())?
|
||||
.value)
|
||||
}
|
||||
|
||||
|
@ -1155,7 +1170,7 @@ impl RpcClient {
|
|||
/// Request the balance of the account `pubkey`.
|
||||
pub fn get_balance(&self, pubkey: &Pubkey) -> ClientResult<u64> {
|
||||
Ok(self
|
||||
.get_balance_with_commitment(pubkey, self.commitment_config)?
|
||||
.get_balance_with_commitment(pubkey, self.commitment())?
|
||||
.value)
|
||||
}
|
||||
|
||||
|
@ -1213,7 +1228,7 @@ impl RpcClient {
|
|||
|
||||
/// Request the transaction count.
|
||||
pub fn get_transaction_count(&self) -> ClientResult<u64> {
|
||||
self.get_transaction_count_with_commitment(self.commitment_config)
|
||||
self.get_transaction_count_with_commitment(self.commitment())
|
||||
}
|
||||
|
||||
pub fn get_transaction_count_with_commitment(
|
||||
|
@ -1228,7 +1243,7 @@ impl RpcClient {
|
|||
|
||||
pub fn get_recent_blockhash(&self) -> ClientResult<(Hash, FeeCalculator)> {
|
||||
let (blockhash, fee_calculator, _last_valid_slot) = self
|
||||
.get_recent_blockhash_with_commitment(self.commitment_config)?
|
||||
.get_recent_blockhash_with_commitment(self.commitment())?
|
||||
.value;
|
||||
Ok((blockhash, fee_calculator))
|
||||
}
|
||||
|
@ -1301,7 +1316,7 @@ impl RpcClient {
|
|||
blockhash: &Hash,
|
||||
) -> ClientResult<Option<FeeCalculator>> {
|
||||
Ok(self
|
||||
.get_fee_calculator_for_blockhash_with_commitment(blockhash, self.commitment_config)?
|
||||
.get_fee_calculator_for_blockhash_with_commitment(blockhash, self.commitment())?
|
||||
.value)
|
||||
}
|
||||
|
||||
|
@ -1383,7 +1398,7 @@ impl RpcClient {
|
|||
|
||||
pub fn get_token_account(&self, pubkey: &Pubkey) -> ClientResult<Option<UiTokenAccount>> {
|
||||
Ok(self
|
||||
.get_token_account_with_commitment(pubkey, self.commitment_config)?
|
||||
.get_token_account_with_commitment(pubkey, self.commitment())?
|
||||
.value)
|
||||
}
|
||||
|
||||
|
@ -1444,7 +1459,7 @@ impl RpcClient {
|
|||
|
||||
pub fn get_token_account_balance(&self, pubkey: &Pubkey) -> ClientResult<UiTokenAmount> {
|
||||
Ok(self
|
||||
.get_token_account_balance_with_commitment(pubkey, self.commitment_config)?
|
||||
.get_token_account_balance_with_commitment(pubkey, self.commitment())?
|
||||
.value)
|
||||
}
|
||||
|
||||
|
@ -1471,7 +1486,7 @@ impl RpcClient {
|
|||
.get_token_accounts_by_delegate_with_commitment(
|
||||
delegate,
|
||||
token_account_filter,
|
||||
self.commitment_config,
|
||||
self.commitment(),
|
||||
)?
|
||||
.value)
|
||||
}
|
||||
|
@ -1510,7 +1525,7 @@ impl RpcClient {
|
|||
.get_token_accounts_by_owner_with_commitment(
|
||||
owner,
|
||||
token_account_filter,
|
||||
self.commitment_config,
|
||||
self.commitment(),
|
||||
)?
|
||||
.value)
|
||||
}
|
||||
|
@ -1542,7 +1557,7 @@ impl RpcClient {
|
|||
|
||||
pub fn get_token_supply(&self, mint: &Pubkey) -> ClientResult<UiTokenAmount> {
|
||||
Ok(self
|
||||
.get_token_supply_with_commitment(mint, self.commitment_config)?
|
||||
.get_token_supply_with_commitment(mint, self.commitment())?
|
||||
.value)
|
||||
}
|
||||
|
||||
|
@ -1565,7 +1580,7 @@ impl RpcClient {
|
|||
pubkey,
|
||||
lamports,
|
||||
RpcRequestAirdropConfig {
|
||||
commitment: Some(self.commitment_config),
|
||||
commitment: Some(self.commitment()),
|
||||
..RpcRequestAirdropConfig::default()
|
||||
},
|
||||
)
|
||||
|
@ -1581,7 +1596,7 @@ impl RpcClient {
|
|||
pubkey,
|
||||
lamports,
|
||||
RpcRequestAirdropConfig {
|
||||
commitment: Some(self.commitment_config),
|
||||
commitment: Some(self.commitment()),
|
||||
recent_blockhash: Some(recent_blockhash.to_string()),
|
||||
},
|
||||
)
|
||||
|
@ -1684,7 +1699,7 @@ impl RpcClient {
|
|||
|
||||
/// Poll the server to confirm a transaction.
|
||||
pub fn poll_for_signature(&self, signature: &Signature) -> ClientResult<()> {
|
||||
self.poll_for_signature_with_commitment(signature, self.commitment_config)
|
||||
self.poll_for_signature_with_commitment(signature, self.commitment())
|
||||
}
|
||||
|
||||
/// Poll the server to confirm a transaction.
|
||||
|
@ -1794,7 +1809,7 @@ impl RpcClient {
|
|||
) -> ClientResult<Signature> {
|
||||
self.send_and_confirm_transaction_with_spinner_and_commitment(
|
||||
transaction,
|
||||
self.commitment_config,
|
||||
self.commitment(),
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -1850,19 +1865,25 @@ impl RpcClient {
|
|||
"[{}/{}] Finalizing transaction {}",
|
||||
confirmations, desired_confirmations, signature,
|
||||
));
|
||||
|
||||
let now = Instant::now();
|
||||
let confirm_transaction_initial_timeout = self
|
||||
.config
|
||||
.confirm_transaction_initial_timeout
|
||||
.unwrap_or_default();
|
||||
let (signature, status) = loop {
|
||||
// Get recent commitment in order to count confirmations for successful transactions
|
||||
let status = self
|
||||
.get_signature_status_with_commitment(signature, CommitmentConfig::processed())?;
|
||||
if status.is_none() {
|
||||
if self
|
||||
let blockhash_not_found = self
|
||||
.get_fee_calculator_for_blockhash_with_commitment(
|
||||
recent_blockhash,
|
||||
CommitmentConfig::processed(),
|
||||
)?
|
||||
.value
|
||||
.is_none()
|
||||
{
|
||||
.is_none();
|
||||
if blockhash_not_found && now.elapsed() >= confirm_transaction_initial_timeout {
|
||||
break (signature, status);
|
||||
}
|
||||
} else {
|
||||
|
@ -1933,6 +1954,26 @@ impl RpcClient {
|
|||
}
|
||||
}
|
||||
|
||||
fn serialize_encode_transaction(
|
||||
transaction: &Transaction,
|
||||
encoding: UiTransactionEncoding,
|
||||
) -> ClientResult<String> {
|
||||
let serialized = serialize(transaction)
|
||||
.map_err(|e| ClientErrorKind::Custom(format!("transaction serialization failed: {}", e)))?;
|
||||
let encoded = match encoding {
|
||||
UiTransactionEncoding::Base58 => bs58::encode(serialized).into_string(),
|
||||
UiTransactionEncoding::Base64 => base64::encode(serialized),
|
||||
_ => {
|
||||
return Err(ClientErrorKind::Custom(format!(
|
||||
"unsupported transaction encoding: {}. Supported encodings: base58, base64",
|
||||
encoding
|
||||
))
|
||||
.into())
|
||||
}
|
||||
};
|
||||
Ok(encoded)
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct GetConfirmedSignaturesForAddress2Config {
|
||||
pub before: Option<Signature>,
|
||||
|
|
Loading…
Reference in New Issue