diff --git a/cli/src/cli.rs b/cli/src/cli.rs index a3a9e96a41..9ddffa1d85 100644 --- a/cli/src/cli.rs +++ b/cli/src/cli.rs @@ -1153,7 +1153,7 @@ fn process_balance( } fn process_confirm(rpc_client: &RpcClient, signature: &Signature) -> ProcessResult { - match rpc_client.get_signature_status(&signature.to_string()) { + match rpc_client.get_signature_status(&signature) { Ok(status) => { if let Some(result) = status { match result { @@ -2165,7 +2165,7 @@ pub fn request_and_confirm_airdrop( log_instruction_custom_error::(result) } -pub fn log_instruction_custom_error(result: ClientResult) -> ProcessResult +pub fn log_instruction_custom_error(result: ClientResult) -> ProcessResult where E: 'static + std::error::Error + DecodeError + FromPrimitive, { @@ -2182,7 +2182,7 @@ where } Err(err.into()) } - Ok(sig) => Ok(sig), + Ok(sig) => Ok(sig.to_string()), } } diff --git a/cli/src/storage.rs b/cli/src/storage.rs index 816d7d0700..6be99dcfbd 100644 --- a/cli/src/storage.rs +++ b/cli/src/storage.rs @@ -266,8 +266,8 @@ pub fn process_claim_storage_reward( &fee_calculator, &tx.message, )?; - let signature_str = rpc_client.send_and_confirm_transaction_with_spinner(&mut tx, &signers)?; - Ok(signature_str) + let signature = rpc_client.send_and_confirm_transaction_with_spinner(&mut tx, &signers)?; + Ok(signature.to_string()) } pub fn process_show_storage_account( diff --git a/client/src/mock_rpc_client_request.rs b/client/src/mock_rpc_client_request.rs index 141aa678aa..f8a0e28caa 100644 --- a/client/src/mock_rpc_client_request.rs +++ b/client/src/mock_rpc_client_request.rs @@ -40,7 +40,7 @@ impl GenericRpcClientRequest for MockRpcClientRequest { fn send( &self, request: &RpcRequest, - params: serde_json::Value, + _params: serde_json::Value, _retries: usize, ) -> Result { if let Some(value) = self.mocks.write().unwrap().remove(request) { @@ -50,17 +50,6 @@ impl GenericRpcClientRequest for MockRpcClientRequest { return Ok(Value::Null); } let val = match request { - RpcRequest::ConfirmTransaction => { - if let Some(params_array) = params.as_array() { - if let Value::String(param_string) = ¶ms_array[0] { - Value::Bool(param_string == SIGNATURE) - } else { - Value::Null - } - } else { - Value::Null - } - } RpcRequest::GetBalance => serde_json::to_value(Response { context: RpcResponseContext { slot: 1 }, value: Value::Number(Number::from(50)), @@ -87,21 +76,6 @@ impl GenericRpcClientRequest for MockRpcClientRequest { context: RpcResponseContext { slot: 1 }, value: serde_json::to_value(FeeRateGovernor::default()).unwrap(), })?, - RpcRequest::GetSignatureStatus => { - let response: Option> = if self.url == "account_in_use" { - Some(Err(TransactionError::AccountInUse)) - } else if self.url == "instruction_error" { - Some(Err(TransactionError::InstructionError( - 0, - InstructionError::UninitializedAccount, - ))) - } else if self.url == "sig_not_found" { - None - } else { - Some(Ok(())) - }; - serde_json::to_value(response).unwrap() - } RpcRequest::GetSignatureStatuses => { let status: transaction::Result<()> = if self.url == "account_in_use" { Err(TransactionError::AccountInUse) diff --git a/client/src/rpc_client.rs b/client/src/rpc_client.rs index d95d562614..e446f8c97b 100644 --- a/client/src/rpc_client.rs +++ b/client/src/rpc_client.rs @@ -32,7 +32,6 @@ use solana_vote_program::vote_state::MAX_LOCKOUT_HISTORY; use std::{ error, net::SocketAddr, - str::FromStr, thread::sleep, time::{Duration, Instant}, }; @@ -71,7 +70,7 @@ impl RpcClient { } } - pub fn confirm_transaction(&self, signature: &str) -> ClientResult { + pub fn confirm_transaction(&self, signature: &Signature) -> ClientResult { Ok(self .confirm_transaction_with_commitment(signature, CommitmentConfig::default())? .value) @@ -79,44 +78,62 @@ impl RpcClient { pub fn confirm_transaction_with_commitment( &self, - signature: &str, + signature: &Signature, commitment_config: CommitmentConfig, ) -> RpcResult { - let response = self - .client - .send( - &RpcRequest::ConfirmTransaction, - json!([signature, commitment_config]), - 0, - ) - .map_err(|err| err.into_with_command("ConfirmTransaction"))?; + let Response { context, value } = + self.get_signature_statuses_with_commitment(&[*signature], commitment_config)?; - serde_json::from_value::>(response) - .map_err(|err| ClientError::new_with_command(err.into(), "ConfirmTransaction")) + Ok(Response { + context, + value: value[0] + .as_ref() + .map(|result| result.status.is_ok()) + .unwrap_or_default(), + }) } - pub fn send_transaction(&self, transaction: &Transaction) -> ClientResult { + pub fn send_transaction(&self, transaction: &Transaction) -> ClientResult { let serialized_encoded = bs58::encode(serialize(transaction).unwrap()).into_string(); - let signature = + let response = self.client .send(&RpcRequest::SendTransaction, json!([serialized_encoded]), 5)?; - if signature.as_str().is_none() { - Err(RpcError::ForUser("Received result of an unexpected type".to_string()).into()) - } else { - Ok(signature.as_str().unwrap().to_string()) + + match response.as_str() { + None => { + Err(RpcError::ForUser("Received result of an unexpected type".to_string()).into()) + } + Some(signature_base58_str) => signature_base58_str + .parse::() + .map_err(|err| RpcError::ParseError(err.to_string()).into()), } } pub fn get_signature_status( &self, - signature: &str, + signature: &Signature, ) -> ClientResult>> { self.get_signature_status_with_commitment(signature, CommitmentConfig::default()) } + pub fn get_signature_statuses_with_commitment( + &self, + signatures: &[Signature], + commitment_config: CommitmentConfig, + ) -> RpcResult>> { + let signatures: Vec<_> = signatures.iter().map(|s| s.to_string()).collect(); + let signature_status = self.client.send( + &RpcRequest::GetSignatureStatuses, + json!([&signatures, commitment_config]), + 5, + )?; + Ok(serde_json::from_value(signature_status) + .map_err(|err| ClientError::new_with_command(err.into(), "GetSignatureStatuses"))?) + } + pub fn get_signature_status_with_commitment( &self, - signature: &str, + signature: &Signature, commitment_config: CommitmentConfig, ) -> ClientResult>> { let signature_status = self.client.send( @@ -334,13 +351,13 @@ impl RpcClient { &self, transaction: &mut Transaction, signer_keys: &T, - ) -> ClientResult { + ) -> ClientResult { let mut send_retries = 20; loop { let mut status_retries = 15; - let signature_str = self.send_transaction(transaction)?; + let signature = self.send_transaction(transaction)?; let status = loop { - let status = self.get_signature_status(&signature_str)?; + let status = self.get_signature_status(&signature)?; if status.is_none() { status_retries -= 1; if status_retries == 0 { @@ -356,7 +373,7 @@ impl RpcClient { }; send_retries = if let Some(result) = status.clone() { match result { - Ok(_) => return Ok(signature_str), + Ok(_) => return Ok(signature), Err(TransactionError::AccountInUse) => { // Fetch a new blockhash and re-sign the transaction before sending it again self.resign_transaction(transaction, signer_keys)?; @@ -818,10 +835,9 @@ impl RpcClient { ) -> ClientResult<()> { let now = Instant::now(); loop { - if let Ok(Some(_)) = self.get_signature_status_with_commitment( - &signature.to_string(), - commitment_config.clone(), - ) { + if let Ok(Some(_)) = + self.get_signature_status_with_commitment(&signature, commitment_config.clone()) + { break; } if now.elapsed().as_secs() > 15 { @@ -841,14 +857,13 @@ impl RpcClient { trace!("check_signature: {:?}", signature); for _ in 0..30 { - let response = self.client.send( - &RpcRequest::ConfirmTransaction, - json!([signature.to_string(), CommitmentConfig::recent()]), - 0, - ); - + let response = + self.confirm_transaction_with_commitment(signature, CommitmentConfig::recent()); match response { - Ok(Value::Bool(signature_status)) => { + Ok(Response { + value: signature_status, + .. + }) => { if signature_status { trace!("Response found signature"); } else { @@ -857,12 +872,6 @@ impl RpcClient { return signature_status; } - Ok(other) => { - debug!( - "check_signature request failed, expected bool, got: {:?}", - other - ); - } Err(err) => { debug!("check_signature request failed: {:?}", err); } @@ -959,7 +968,7 @@ impl RpcClient { &self, transaction: &mut Transaction, signer_keys: &T, - ) -> ClientResult { + ) -> ClientResult { let mut confirmations = 0; let progress_bar = new_spinner_progress_bar(); @@ -970,23 +979,21 @@ impl RpcClient { )); let mut send_retries = 20; - let signature_str = loop { + let signature = loop { let mut status_retries = 15; - let (signature_str, status) = loop { - let signature_str = self.send_transaction(transaction)?; + let (signature, status) = loop { + let signature = self.send_transaction(transaction)?; // Get recent commitment in order to count confirmations for successful transactions - let status = self.get_signature_status_with_commitment( - &signature_str, - CommitmentConfig::recent(), - )?; + let status = self + .get_signature_status_with_commitment(&signature, CommitmentConfig::recent())?; if status.is_none() { status_retries -= 1; if status_retries == 0 { - break (signature_str, status); + break (signature, status); } } else { - break (signature_str, status); + break (signature, status); } if cfg!(not(test)) { @@ -1011,7 +1018,7 @@ impl RpcClient { if let Some(result) = status { match result { Ok(_) => { - break signature_str; + break signature; } Err(err) => { return Err(err.into()); @@ -1024,19 +1031,13 @@ impl RpcClient { } } }; - let signature = Signature::from_str(&signature_str).map_err(|_| { - ClientError::from(ClientErrorKind::Custom(format!( - "Returned string {} cannot be parsed as a signature", - signature_str - ))) - })?; loop { // Return when default (max) commitment is reached // Failed transactions have already been eliminated, `is_some` check is sufficient - if self.get_signature_status(&signature_str)?.is_some() { + if self.get_signature_status(&signature)?.is_some() { progress_bar.set_message("Transaction confirmed"); progress_bar.finish_and_clear(); - return Ok(signature_str); + return Ok(signature); } progress_bar.set_message(&format!( "[{}/{}] Waiting for confirmations", @@ -1198,7 +1199,7 @@ mod tests { let tx = system_transaction::transfer(&key, &to, 50, blockhash); let signature = rpc_client.send_transaction(&tx); - assert_eq!(signature.unwrap(), SIGNATURE.to_string()); + assert_eq!(signature.unwrap(), SIGNATURE.parse().unwrap()); let rpc_client = RpcClient::new_mock("fails".to_string()); @@ -1221,18 +1222,17 @@ mod tests { #[test] fn test_get_signature_status() { + let signature = Signature::default(); + let rpc_client = RpcClient::new_mock("succeeds".to_string()); - let signature = "good_signature"; let status = rpc_client.get_signature_status(&signature).unwrap(); assert_eq!(status, Some(Ok(()))); let rpc_client = RpcClient::new_mock("sig_not_found".to_string()); - let signature = "sig_not_found"; let status = rpc_client.get_signature_status(&signature).unwrap(); assert_eq!(status, None); let rpc_client = RpcClient::new_mock("account_in_use".to_string()); - let signature = "account_in_use"; let status = rpc_client.get_signature_status(&signature).unwrap(); assert_eq!(status, Some(Err(TransactionError::AccountInUse))); } diff --git a/client/src/rpc_request.rs b/client/src/rpc_request.rs index 6ba9ecdf2f..71bd1bc56a 100644 --- a/client/src/rpc_request.rs +++ b/client/src/rpc_request.rs @@ -3,7 +3,6 @@ use thiserror::Error; #[derive(Debug, PartialEq, Eq, Hash)] pub enum RpcRequest { - ConfirmTransaction, DeregisterNode, ValidatorExit, GetAccountInfo, @@ -22,7 +21,6 @@ pub enum RpcRequest { GetRecentBlockhash, GetFeeCalculatorForBlockhash, GetFeeRateGovernor, - GetSignatureStatus, GetSignatureStatuses, GetSlot, GetSlotLeader, @@ -45,7 +43,6 @@ impl RpcRequest { pub(crate) fn build_request_json(&self, id: u64, params: Value) -> Value { let jsonrpc = "2.0"; let method = match self { - RpcRequest::ConfirmTransaction => "confirmTransaction", RpcRequest::DeregisterNode => "deregisterNode", RpcRequest::ValidatorExit => "validatorExit", RpcRequest::GetAccountInfo => "getAccountInfo", @@ -64,7 +61,6 @@ impl RpcRequest { RpcRequest::GetRecentBlockhash => "getRecentBlockhash", RpcRequest::GetFeeCalculatorForBlockhash => "getFeeCalculatorForBlockhash", RpcRequest::GetFeeRateGovernor => "getFeeRateGovernor", - RpcRequest::GetSignatureStatus => "getSignatureStatus", RpcRequest::GetSignatureStatuses => "getSignatureStatuses", RpcRequest::GetSlot => "getSlot", RpcRequest::GetSlotLeader => "getSlotLeader", diff --git a/client/src/thin_client.rs b/client/src/thin_client.rs index e3c601bddc..2384a66a35 100644 --- a/client/src/thin_client.rs +++ b/client/src/thin_client.rs @@ -471,7 +471,7 @@ impl SyncClient for ThinClient { ) -> TransportResult>> { let status = self .rpc_client() - .get_signature_status(&signature.to_string()) + .get_signature_status(&signature) .map_err(|err| { io::Error::new( io::ErrorKind::Other, @@ -488,7 +488,7 @@ impl SyncClient for ThinClient { ) -> TransportResult>> { let status = self .rpc_client() - .get_signature_status_with_commitment(&signature.to_string(), commitment_config) + .get_signature_status_with_commitment(&signature, commitment_config) .map_err(|err| { io::Error::new( io::ErrorKind::Other, diff --git a/core/src/rpc.rs b/core/src/rpc.rs index a6d5c04509..1adf300602 100644 --- a/core/src/rpc.rs +++ b/core/src/rpc.rs @@ -422,7 +422,7 @@ impl JsonRpcRequestProcessor { self.bank(commitment).get_signature_status(&signature) } - pub fn get_signature_statuses( + pub fn get_signature_statuses_with_commitment( &self, signatures: Vec, commitment: Option, @@ -493,6 +493,7 @@ impl Metadata for Meta {} pub trait RpcSol { type Metadata; + // DEPRECATED #[rpc(meta, name = "confirmTransaction")] fn confirm_transaction( &self, @@ -501,6 +502,24 @@ pub trait RpcSol { commitment: Option, ) -> RpcResponse; + // DEPRECATED + #[rpc(meta, name = "getSignatureStatus")] + fn get_signature_status( + &self, + meta: Self::Metadata, + signature_str: String, + commitment: Option, + ) -> Result>>; + + // DEPRECATED (used by Trust Wallet) + #[rpc(meta, name = "getSignatureConfirmation")] + fn get_signature_confirmation( + &self, + meta: Self::Metadata, + signature_str: String, + commitment: Option, + ) -> Result>; + #[rpc(meta, name = "getAccountInfo")] fn get_account_info( &self, @@ -588,24 +607,8 @@ pub trait RpcSol { #[rpc(meta, name = "getFeeRateGovernor")] fn get_fee_rate_governor(&self, meta: Self::Metadata) -> RpcResponse; - #[rpc(meta, name = "getSignatureConfirmation")] - fn get_signature_confirmation( - &self, - meta: Self::Metadata, - signature_str: String, - commitment: Option, - ) -> Result>; - - #[rpc(meta, name = "getSignatureStatus")] - fn get_signature_status( - &self, - meta: Self::Metadata, - signature_str: String, - commitment: Option, - ) -> Result>>; - #[rpc(meta, name = "getSignatureStatuses")] - fn get_signature_statuses( + fn get_signature_statuses_with_commitment( &self, meta: Self::Metadata, signature_strs: Vec, @@ -967,7 +970,7 @@ impl RpcSol for RpcSolImpl { .get_signature_status(signature, commitment)) } - fn get_signature_statuses( + fn get_signature_statuses_with_commitment( &self, meta: Self::Metadata, signature_strs: Vec, @@ -980,7 +983,7 @@ impl RpcSol for RpcSolImpl { meta.request_processor .read() .unwrap() - .get_signature_statuses(signatures, commitment) + .get_signature_statuses_with_commitment(signatures, commitment) } fn get_slot(&self, meta: Self::Metadata, commitment: Option) -> Result { @@ -1073,7 +1076,7 @@ impl RpcSol for RpcSolImpl { .request_processor .read() .unwrap() - .get_signature_statuses(vec![signature], commitment.clone())? + .get_signature_statuses_with_commitment(vec![signature], commitment.clone())? .value[0] .clone() .map(|x| x.status); diff --git a/core/tests/client.rs b/core/tests/client.rs index bb9c456af7..7b1d9645df 100644 --- a/core/tests/client.rs +++ b/core/tests/client.rs @@ -58,7 +58,7 @@ fn test_rpc_client() { let now = Instant::now(); while now.elapsed().as_secs() <= 20 { let response = client - .confirm_transaction_with_commitment(signature.as_str(), CommitmentConfig::default()) + .confirm_transaction_with_commitment(&signature, CommitmentConfig::default()) .unwrap(); if response.value { diff --git a/docs/src/apps/jsonrpc-api.md b/docs/src/apps/jsonrpc-api.md index 0e3824dd85..835210cb34 100644 --- a/docs/src/apps/jsonrpc-api.md +++ b/docs/src/apps/jsonrpc-api.md @@ -14,7 +14,6 @@ To interact with a Solana node inside a JavaScript application, use the [solana- ## Methods -* [confirmTransaction](jsonrpc-api.md#confirmtransaction) * [getAccountInfo](jsonrpc-api.md#getaccountinfo) * [getBalance](jsonrpc-api.md#getbalance) * [getBlockCommitment](jsonrpc-api.md#getblockcommitment) @@ -33,7 +32,6 @@ To interact with a Solana node inside a JavaScript application, use the [solana- * [getMinimumBalanceForRentExemption](jsonrpc-api.md#getminimumbalanceforrentexemption) * [getProgramAccounts](jsonrpc-api.md#getprogramaccounts) * [getRecentBlockhash](jsonrpc-api.md#getrecentblockhash) -* [getSignatureStatus](jsonrpc-api.md#getsignaturestatus) * [getSignatureStatuses](jsonrpc-api.md#getsignaturestatuses) * [getSlot](jsonrpc-api.md#getslot) * [getSlotLeader](jsonrpc-api.md#getslotleader) @@ -117,29 +115,6 @@ Many methods that take a commitment parameter return an RpcResponse JSON object ## JSON RPC API Reference -### confirmTransaction - -Returns a transaction receipt. This method only searches the recent status cache of signatures, which retains all active slots plus `MAX_RECENT_BLOCKHASHES` rooted slots. - -#### Parameters: - -* `` - Signature of Transaction to confirm, as base-58 encoded string -* `` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment) - -#### Results: - -* `RpcResponse` - RpcResponse JSON object with `value` field set to Transaction status, boolean true if Transaction is confirmed - -#### Example: - -```bash -// Request -curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0", "id":1, "method":"confirmTransaction", "params":["5VERv8NMvzbJMEkV8xnrLkEaWRtSz9CosKDYjCJjBRnbJLgp8uirBgmQpjKhoR4tjF3ZpRzrFmBV6UjKdiSZkQUW"]}' http://localhost:8899 - -// Result -{"jsonrpc":"2.0","result":{"context":{"slot":1},"value":true},"id":1} -``` - ### getAccountInfo Returns all information associated with the account of provided Pubkey @@ -656,35 +631,9 @@ curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "m {"jsonrpc":"2.0","result":{"context":{"slot":1},"value":{"blockhash":"CSymwgTNX1j3E4qhKfJAUE41nBWEwXufoYryPbkde5RR","feeCalculator":{"burnPercent":50,"lamportsPerSignature":5000,"maxLamportsPerSignature":10000,"minLamportsPerSignature":5000,"targetLamportsPerSignature":1000,"targetSignaturesPerSlot":20000}}},"id":1} ``` -### getSignatureStatus - -Returns the status of a given signature. This method is similar to [confirmTransaction](jsonrpc-api.md#confirmtransaction) but provides more resolution for error events. This method only searches the recent status cache of signatures, which retains all active slots plus `MAX_RECENT_BLOCKHASHES` rooted slots. - -#### Parameters: - -* `` - Signature of Transaction to confirm, as base-58 encoded string -* `` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment) - -#### Results: - -* `` - Unknown transaction -* `` - Transaction status: - * `"Ok": ` - Transaction was successful - * `"Err": ` - Transaction failed with TransactionError [TransactionError definitions](https://github.com/solana-labs/solana/blob/master/sdk/src/transaction.rs#L14) - -#### Example: - -```bash -// Request -curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0", "id":1, "method":"getSignatureStatus", "params":["5VERv8NMvzbJMEkV8xnrLkEaWRtSz9CosKDYjCJjBRnbJLgp8uirBgmQpjKhoR4tjF3ZpRzrFmBV6UjKdiSZkQUW"]}' http://localhost:8899 - -// Result -{"jsonrpc":"2.0","result":{"Ok": null},"id":1} -``` - ### getSignatureStatuses -Returns the statuses of a list of signatures. This method is similar to [confirmTransaction](jsonrpc-api.md#confirmtransaction) but provides more resolution for error events. This method only searches the recent status cache of signatures, which retains all active slots plus `MAX_RECENT_BLOCKHASHES` rooted slots. +Returns the statuses of a list of signatures. This method only searches the recent status cache of signatures, which retains statuses for all active slots plus `MAX_RECENT_BLOCKHASHES` rooted slots. #### Parameters: @@ -713,7 +662,7 @@ An array of: ```bash // Request -curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0", "id":1, "method":"getSignatureStatus", "params":[["5VERv8NMvzbJMEkV8xnrLkEaWRtSz9CosKDYjCJjBRnbJLgp8uirBgmQpjKhoR4tjF3ZpRzrFmBV6UjKdiSZkQUW", "5j7s6NiJS3JAkvgkoc18WVAsiSaci2pxB2A6ueCJP4tprA2TFg9wSyTLeYouxPBJEMzJinENTkpA52YStRW5Dia7"]]]}' http://localhost:8899 +curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0", "id":1, "method":"getSignatureStatuses", "params":[["5VERv8NMvzbJMEkV8xnrLkEaWRtSz9CosKDYjCJjBRnbJLgp8uirBgmQpjKhoR4tjF3ZpRzrFmBV6UjKdiSZkQUW", "5j7s6NiJS3JAkvgkoc18WVAsiSaci2pxB2A6ueCJP4tprA2TFg9wSyTLeYouxPBJEMzJinENTkpA52YStRW5Dia7"]]]}' http://localhost:8899 // Result {"jsonrpc":"2.0","result":{"context":{"slot":82},"value":[{"slot": 72, "confirmations": 10, "err": null, "status": {"Ok": null}}, null]},"id":1}