solana vote-account
/solana stake-account
now works with RPC servers without --enable-rpc-transaction-history
(bp #12826) (#12848)
* Implementation-defined RPC server errors are now accessible to client/ users (cherry picked from commit247228ee61
) * Cleanly handle RPC servers that don't have --enable-rpc-transaction-history enabled (cherry picked from commit14d793b22c
) Co-authored-by: Michael Vines <mvines@gmail.com>
This commit is contained in:
@ -23,8 +23,12 @@ use solana_cli_output::{
|
|||||||
CliStakeType,
|
CliStakeType,
|
||||||
};
|
};
|
||||||
use solana_client::{
|
use solana_client::{
|
||||||
blockhash_query::BlockhashQuery, nonce_utils, rpc_client::RpcClient,
|
blockhash_query::BlockhashQuery,
|
||||||
rpc_request::DELINQUENT_VALIDATOR_SLOT_DISTANCE,
|
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_remote_wallet::remote_wallet::RemoteWalletManager;
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
@ -1605,10 +1609,26 @@ pub(crate) fn fetch_epoch_rewards(
|
|||||||
.get(0)
|
.get(0)
|
||||||
.ok_or_else(|| format!("Unable to fetch first confirmed block for epoch {}", epoch))?;
|
.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,
|
first_confirmed_block_in_epoch,
|
||||||
solana_transaction_status::UiTransactionEncoding::Base64,
|
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 {
|
let epoch_start_time = if let Some(block_time) = first_confirmed_block.block_time {
|
||||||
block_time
|
block_time
|
||||||
|
@ -50,10 +50,10 @@ impl Into<TransportError> for ClientErrorKind {
|
|||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
#[error("{kind}")]
|
#[error("{kind}")]
|
||||||
pub struct ClientError {
|
pub struct ClientError {
|
||||||
request: Option<rpc_request::RpcRequest>,
|
pub request: Option<rpc_request::RpcRequest>,
|
||||||
|
|
||||||
#[source]
|
#[source]
|
||||||
kind: ClientErrorKind,
|
pub kind: ClientErrorKind,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ClientError {
|
impl ClientError {
|
||||||
|
@ -27,6 +27,13 @@ impl HttpSender {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
struct RpcErrorObject {
|
||||||
|
code: i64,
|
||||||
|
message: String,
|
||||||
|
/*data field omitted*/
|
||||||
|
}
|
||||||
|
|
||||||
impl RpcSender for HttpSender {
|
impl RpcSender for HttpSender {
|
||||||
fn send(&self, request: RpcRequest, params: serde_json::Value) -> Result<serde_json::Value> {
|
fn send(&self, request: RpcRequest, params: serde_json::Value) -> Result<serde_json::Value> {
|
||||||
// Concurrent requests are not supported so reuse the same request id for all requests
|
// 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()?)?;
|
let json: serde_json::Value = serde_json::from_str(&response.text()?)?;
|
||||||
if json["error"].is_object() {
|
if json["error"].is_object() {
|
||||||
return Err(RpcError::RpcRequestError(format!(
|
return match serde_json::from_value::<RpcErrorObject>(json["error"].clone())
|
||||||
"RPC Error response: {}",
|
{
|
||||||
serde_json::to_string(&json["error"]).unwrap()
|
Ok(rpc_error_object) => Err(RpcError::RpcResponseError {
|
||||||
))
|
code: rpc_error_object.code,
|
||||||
.into());
|
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());
|
return Ok(json["result"].clone());
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@ pub mod perf_utils;
|
|||||||
pub mod pubsub_client;
|
pub mod pubsub_client;
|
||||||
pub mod rpc_client;
|
pub mod rpc_client;
|
||||||
pub mod rpc_config;
|
pub mod rpc_config;
|
||||||
|
pub mod rpc_custom_error;
|
||||||
pub mod rpc_filter;
|
pub mod rpc_filter;
|
||||||
pub mod rpc_request;
|
pub mod rpc_request;
|
||||||
pub mod rpc_response;
|
pub mod rpc_response;
|
||||||
|
@ -1,13 +1,15 @@
|
|||||||
|
//! Implementation defined RPC server errors
|
||||||
|
|
||||||
|
use crate::rpc_response::RpcSimulateTransactionResult;
|
||||||
use jsonrpc_core::{Error, ErrorCode};
|
use jsonrpc_core::{Error, ErrorCode};
|
||||||
use solana_client::rpc_response::RpcSimulateTransactionResult;
|
|
||||||
use solana_sdk::clock::Slot;
|
use solana_sdk::clock::Slot;
|
||||||
|
|
||||||
const JSON_RPC_SERVER_ERROR_1: i64 = -32001;
|
pub const JSON_RPC_SERVER_ERROR_BLOCK_CLEANED_UP: i64 = -32001;
|
||||||
const JSON_RPC_SERVER_ERROR_2: i64 = -32002;
|
pub const JSON_RPC_SERVER_ERROR_SEND_TRANSACTION_PREFLIGHT_FAILURE: i64 = -32002;
|
||||||
const JSON_RPC_SERVER_ERROR_3: i64 = -32003;
|
pub const JSON_RPC_SERVER_ERROR_TRANSACTION_SIGNATURE_VERIFICATION_FAILURE: i64 = -32003;
|
||||||
const JSON_RPC_SERVER_ERROR_4: i64 = -32004;
|
pub const JSON_RPC_SERVER_ERROR_BLOCK_NOT_AVAILABLE: i64 = -32004;
|
||||||
const JSON_RPC_SERVER_ERROR_5: i64 = -32005;
|
pub const JSON_RPC_SERVER_ERROR_NODE_UNHEALTHLY: i64 = -32005;
|
||||||
const JSON_RPC_SERVER_ERROR_6: i64 = -32006;
|
pub const JSON_RPC_SERVER_ERROR_TRANSACTION_PRECOMPILE_VERIFICATION_FAILURE: i64 = -32006;
|
||||||
|
|
||||||
pub enum RpcCustomError {
|
pub enum RpcCustomError {
|
||||||
BlockCleanedUp {
|
BlockCleanedUp {
|
||||||
@ -33,7 +35,7 @@ impl From<RpcCustomError> for Error {
|
|||||||
slot,
|
slot,
|
||||||
first_available_block,
|
first_available_block,
|
||||||
} => Self {
|
} => Self {
|
||||||
code: ErrorCode::ServerError(JSON_RPC_SERVER_ERROR_1),
|
code: ErrorCode::ServerError(JSON_RPC_SERVER_ERROR_BLOCK_CLEANED_UP),
|
||||||
message: format!(
|
message: format!(
|
||||||
"Block {} cleaned up, does not exist on node. First available block: {}",
|
"Block {} cleaned up, does not exist on node. First available block: {}",
|
||||||
slot, first_available_block,
|
slot, first_available_block,
|
||||||
@ -41,27 +43,33 @@ impl From<RpcCustomError> for Error {
|
|||||||
data: None,
|
data: None,
|
||||||
},
|
},
|
||||||
RpcCustomError::SendTransactionPreflightFailure { message, result } => Self {
|
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,
|
message,
|
||||||
data: Some(serde_json::json!(result)),
|
data: Some(serde_json::json!(result)),
|
||||||
},
|
},
|
||||||
RpcCustomError::TransactionSignatureVerificationFailure => Self {
|
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(),
|
message: "Transaction signature verification failure".to_string(),
|
||||||
data: None,
|
data: None,
|
||||||
},
|
},
|
||||||
RpcCustomError::BlockNotAvailable { slot } => Self {
|
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),
|
message: format!("Block not available for slot {}", slot),
|
||||||
data: None,
|
data: None,
|
||||||
},
|
},
|
||||||
RpcCustomError::RpcNodeUnhealthy => Self {
|
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(),
|
message: "RPC node is unhealthy".to_string(),
|
||||||
data: None,
|
data: None,
|
||||||
},
|
},
|
||||||
RpcCustomError::TransactionPrecompileVerificationFailure(e) => Self {
|
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),
|
message: format!("Transaction precompile verification failure {:?}", e),
|
||||||
data: None,
|
data: None,
|
||||||
},
|
},
|
@ -140,8 +140,10 @@ impl RpcRequest {
|
|||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum RpcError {
|
pub enum RpcError {
|
||||||
#[error("rpc request error: {0}")]
|
#[error("RPC request error: {0}")]
|
||||||
RpcRequestError(String),
|
RpcRequestError(String),
|
||||||
|
#[error("RPC response error {code}: {message}")]
|
||||||
|
RpcResponseError { code: i64, message: String },
|
||||||
#[error("parse error: expected {0}")]
|
#[error("parse error: expected {0}")]
|
||||||
ParseError(String), /* "expected" */
|
ParseError(String), /* "expected" */
|
||||||
// Anything in a `ForUser` needs to die. The caller should be
|
// Anything in a `ForUser` needs to die. The caller should be
|
||||||
|
@ -56,7 +56,6 @@ mod result;
|
|||||||
pub mod retransmit_stage;
|
pub mod retransmit_stage;
|
||||||
pub mod rewards_recorder_service;
|
pub mod rewards_recorder_service;
|
||||||
pub mod rpc;
|
pub mod rpc;
|
||||||
pub mod rpc_error;
|
|
||||||
pub mod rpc_health;
|
pub mod rpc_health;
|
||||||
pub mod rpc_pubsub;
|
pub mod rpc_pubsub;
|
||||||
pub mod rpc_pubsub_service;
|
pub mod rpc_pubsub_service;
|
||||||
|
@ -5,7 +5,6 @@ use crate::{
|
|||||||
contact_info::ContactInfo,
|
contact_info::ContactInfo,
|
||||||
non_circulating_supply::calculate_non_circulating_supply,
|
non_circulating_supply::calculate_non_circulating_supply,
|
||||||
optimistically_confirmed_bank_tracker::OptimisticallyConfirmedBank,
|
optimistically_confirmed_bank_tracker::OptimisticallyConfirmedBank,
|
||||||
rpc_error::RpcCustomError,
|
|
||||||
rpc_health::*,
|
rpc_health::*,
|
||||||
send_transaction_service::{SendTransactionService, TransactionInfo},
|
send_transaction_service::{SendTransactionService, TransactionInfo},
|
||||||
validator::ValidatorExit,
|
validator::ValidatorExit,
|
||||||
@ -23,6 +22,7 @@ use solana_account_decoder::{
|
|||||||
};
|
};
|
||||||
use solana_client::{
|
use solana_client::{
|
||||||
rpc_config::*,
|
rpc_config::*,
|
||||||
|
rpc_custom_error::RpcCustomError,
|
||||||
rpc_filter::{Memcmp, MemcmpEncodedBytes, RpcFilterType},
|
rpc_filter::{Memcmp, MemcmpEncodedBytes, RpcFilterType},
|
||||||
rpc_request::{
|
rpc_request::{
|
||||||
TokenAccountsFilter, DELINQUENT_VALIDATOR_SLOT_DISTANCE, MAX_GET_CONFIRMED_BLOCKS_RANGE,
|
TokenAccountsFilter, DELINQUENT_VALIDATOR_SLOT_DISTANCE, MAX_GET_CONFIRMED_BLOCKS_RANGE,
|
||||||
|
Reference in New Issue
Block a user