diff --git a/cli/src/stake.rs b/cli/src/stake.rs index 36785a61aa..31d9127e89 100644 --- a/cli/src/stake.rs +++ b/cli/src/stake.rs @@ -23,8 +23,12 @@ use solana_cli_output::{ CliStakeType, }; use solana_client::{ - blockhash_query::BlockhashQuery, nonce_utils, rpc_client::RpcClient, - rpc_request::DELINQUENT_VALIDATOR_SLOT_DISTANCE, + blockhash_query::BlockhashQuery, + client_error::{ClientError, ClientErrorKind}, + nonce_utils, + rpc_client::RpcClient, + rpc_custom_error, + rpc_request::{self, DELINQUENT_VALIDATOR_SLOT_DISTANCE}, }; use solana_remote_wallet::remote_wallet::RemoteWalletManager; use solana_sdk::{ @@ -1605,10 +1609,26 @@ pub(crate) fn fetch_epoch_rewards( .get(0) .ok_or_else(|| format!("Unable to fetch first confirmed block for epoch {}", epoch))?; - let first_confirmed_block = rpc_client.get_confirmed_block_with_encoding( + let first_confirmed_block = match rpc_client.get_confirmed_block_with_encoding( first_confirmed_block_in_epoch, solana_transaction_status::UiTransactionEncoding::Base64, - )?; + ) { + Ok(first_confirmed_block) => first_confirmed_block, + Err(ClientError { + kind: + ClientErrorKind::RpcError(rpc_request::RpcError::RpcResponseError { + code: rpc_custom_error::JSON_RPC_SERVER_ERROR_BLOCK_NOT_AVAILABLE, + message: _, + }), + request: _, + }) => { + // RPC node doesn't have this block + break; + } + Err(err) => { + return Err(err.into()); + } + }; let epoch_start_time = if let Some(block_time) = first_confirmed_block.block_time { block_time diff --git a/client/src/client_error.rs b/client/src/client_error.rs index 7397ec5eaf..417d583018 100644 --- a/client/src/client_error.rs +++ b/client/src/client_error.rs @@ -50,10 +50,10 @@ impl Into for ClientErrorKind { #[derive(Error, Debug)] #[error("{kind}")] pub struct ClientError { - request: Option, + pub request: Option, #[source] - kind: ClientErrorKind, + pub kind: ClientErrorKind, } impl ClientError { diff --git a/client/src/http_sender.rs b/client/src/http_sender.rs index ea79d73338..d72122b3a6 100644 --- a/client/src/http_sender.rs +++ b/client/src/http_sender.rs @@ -27,6 +27,13 @@ impl HttpSender { } } +#[derive(Deserialize, Debug)] +struct RpcErrorObject { + code: i64, + message: String, + /*data field omitted*/ +} + impl RpcSender for HttpSender { fn send(&self, request: RpcRequest, params: serde_json::Value) -> Result { // Concurrent requests are not supported so reuse the same request id for all requests @@ -63,11 +70,20 @@ impl RpcSender for HttpSender { let json: serde_json::Value = serde_json::from_str(&response.text()?)?; if json["error"].is_object() { - return Err(RpcError::RpcRequestError(format!( - "RPC Error response: {}", - serde_json::to_string(&json["error"]).unwrap() - )) - .into()); + return match serde_json::from_value::(json["error"].clone()) + { + Ok(rpc_error_object) => Err(RpcError::RpcResponseError { + code: rpc_error_object.code, + message: rpc_error_object.message, + } + .into()), + Err(err) => Err(RpcError::RpcRequestError(format!( + "Failed to deserialize RPC error response: {} [{}]", + serde_json::to_string(&json["error"]).unwrap(), + err + )) + .into()), + }; } return Ok(json["result"].clone()); } diff --git a/client/src/lib.rs b/client/src/lib.rs index 3d70dd0a1a..7e8cce4acf 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -10,6 +10,7 @@ pub mod perf_utils; pub mod pubsub_client; pub mod rpc_client; pub mod rpc_config; +pub mod rpc_custom_error; pub mod rpc_filter; pub mod rpc_request; pub mod rpc_response; diff --git a/core/src/rpc_error.rs b/client/src/rpc_custom_error.rs similarity index 66% rename from core/src/rpc_error.rs rename to client/src/rpc_custom_error.rs index 6529f661c3..b28fac9a68 100644 --- a/core/src/rpc_error.rs +++ b/client/src/rpc_custom_error.rs @@ -1,13 +1,15 @@ +//! Implementation defined RPC server errors + +use crate::rpc_response::RpcSimulateTransactionResult; use jsonrpc_core::{Error, ErrorCode}; -use solana_client::rpc_response::RpcSimulateTransactionResult; use solana_sdk::clock::Slot; -const JSON_RPC_SERVER_ERROR_1: i64 = -32001; -const JSON_RPC_SERVER_ERROR_2: i64 = -32002; -const JSON_RPC_SERVER_ERROR_3: i64 = -32003; -const JSON_RPC_SERVER_ERROR_4: i64 = -32004; -const JSON_RPC_SERVER_ERROR_5: i64 = -32005; -const JSON_RPC_SERVER_ERROR_6: i64 = -32006; +pub const JSON_RPC_SERVER_ERROR_BLOCK_CLEANED_UP: i64 = -32001; +pub const JSON_RPC_SERVER_ERROR_SEND_TRANSACTION_PREFLIGHT_FAILURE: i64 = -32002; +pub const JSON_RPC_SERVER_ERROR_TRANSACTION_SIGNATURE_VERIFICATION_FAILURE: i64 = -32003; +pub const JSON_RPC_SERVER_ERROR_BLOCK_NOT_AVAILABLE: i64 = -32004; +pub const JSON_RPC_SERVER_ERROR_NODE_UNHEALTHLY: i64 = -32005; +pub const JSON_RPC_SERVER_ERROR_TRANSACTION_PRECOMPILE_VERIFICATION_FAILURE: i64 = -32006; pub enum RpcCustomError { BlockCleanedUp { @@ -33,7 +35,7 @@ impl From for Error { slot, first_available_block, } => Self { - code: ErrorCode::ServerError(JSON_RPC_SERVER_ERROR_1), + code: ErrorCode::ServerError(JSON_RPC_SERVER_ERROR_BLOCK_CLEANED_UP), message: format!( "Block {} cleaned up, does not exist on node. First available block: {}", slot, first_available_block, @@ -41,27 +43,33 @@ impl From for Error { data: None, }, RpcCustomError::SendTransactionPreflightFailure { message, result } => Self { - code: ErrorCode::ServerError(JSON_RPC_SERVER_ERROR_2), + code: ErrorCode::ServerError( + JSON_RPC_SERVER_ERROR_SEND_TRANSACTION_PREFLIGHT_FAILURE, + ), message, data: Some(serde_json::json!(result)), }, RpcCustomError::TransactionSignatureVerificationFailure => Self { - code: ErrorCode::ServerError(JSON_RPC_SERVER_ERROR_3), + code: ErrorCode::ServerError( + JSON_RPC_SERVER_ERROR_TRANSACTION_SIGNATURE_VERIFICATION_FAILURE, + ), message: "Transaction signature verification failure".to_string(), data: None, }, RpcCustomError::BlockNotAvailable { slot } => Self { - code: ErrorCode::ServerError(JSON_RPC_SERVER_ERROR_4), + code: ErrorCode::ServerError(JSON_RPC_SERVER_ERROR_BLOCK_NOT_AVAILABLE), message: format!("Block not available for slot {}", slot), data: None, }, RpcCustomError::RpcNodeUnhealthy => Self { - code: ErrorCode::ServerError(JSON_RPC_SERVER_ERROR_5), + code: ErrorCode::ServerError(JSON_RPC_SERVER_ERROR_NODE_UNHEALTHLY), message: "RPC node is unhealthy".to_string(), data: None, }, RpcCustomError::TransactionPrecompileVerificationFailure(e) => Self { - code: ErrorCode::ServerError(JSON_RPC_SERVER_ERROR_6), + code: ErrorCode::ServerError( + JSON_RPC_SERVER_ERROR_TRANSACTION_SIGNATURE_VERIFICATION_FAILURE, + ), message: format!("Transaction precompile verification failure {:?}", e), data: None, }, diff --git a/client/src/rpc_request.rs b/client/src/rpc_request.rs index 66e66a61ec..d973c9fac7 100644 --- a/client/src/rpc_request.rs +++ b/client/src/rpc_request.rs @@ -140,8 +140,10 @@ impl RpcRequest { #[derive(Debug, Error)] pub enum RpcError { - #[error("rpc request error: {0}")] + #[error("RPC request error: {0}")] RpcRequestError(String), + #[error("RPC response error {code}: {message}")] + RpcResponseError { code: i64, message: String }, #[error("parse error: expected {0}")] ParseError(String), /* "expected" */ // Anything in a `ForUser` needs to die. The caller should be diff --git a/core/src/lib.rs b/core/src/lib.rs index eb6c217fba..2a520ca77c 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -56,7 +56,6 @@ mod result; pub mod retransmit_stage; pub mod rewards_recorder_service; pub mod rpc; -pub mod rpc_error; pub mod rpc_health; pub mod rpc_pubsub; pub mod rpc_pubsub_service; diff --git a/core/src/rpc.rs b/core/src/rpc.rs index 04d4a52e8b..dd862b3f5f 100644 --- a/core/src/rpc.rs +++ b/core/src/rpc.rs @@ -5,7 +5,6 @@ use crate::{ contact_info::ContactInfo, non_circulating_supply::calculate_non_circulating_supply, optimistically_confirmed_bank_tracker::OptimisticallyConfirmedBank, - rpc_error::RpcCustomError, rpc_health::*, send_transaction_service::{SendTransactionService, TransactionInfo}, validator::ValidatorExit, @@ -23,6 +22,7 @@ use solana_account_decoder::{ }; use solana_client::{ rpc_config::*, + rpc_custom_error::RpcCustomError, rpc_filter::{Memcmp, MemcmpEncodedBytes, RpcFilterType}, rpc_request::{ TokenAccountsFilter, DELINQUENT_VALIDATOR_SLOT_DISTANCE, MAX_GET_CONFIRMED_BLOCKS_RANGE,