safecoin_client/
rpc_custom_error.rs

1//! Implementation defined RPC server errors
2use {
3    crate::rpc_response::RpcSimulateTransactionResult,
4    jsonrpc_core::{Error, ErrorCode},
5    solana_sdk::clock::Slot,
6    safecoin_transaction_status::EncodeError,
7    thiserror::Error,
8};
9
10// Keep in sync with web3.js/src/errors.ts
11pub const JSON_RPC_SERVER_ERROR_BLOCK_CLEANED_UP: i64 = -32001;
12pub const JSON_RPC_SERVER_ERROR_SEND_TRANSACTION_PREFLIGHT_FAILURE: i64 = -32002;
13pub const JSON_RPC_SERVER_ERROR_TRANSACTION_SIGNATURE_VERIFICATION_FAILURE: i64 = -32003;
14pub const JSON_RPC_SERVER_ERROR_BLOCK_NOT_AVAILABLE: i64 = -32004;
15pub const JSON_RPC_SERVER_ERROR_NODE_UNHEALTHY: i64 = -32005;
16pub const JSON_RPC_SERVER_ERROR_TRANSACTION_PRECOMPILE_VERIFICATION_FAILURE: i64 = -32006;
17pub const JSON_RPC_SERVER_ERROR_SLOT_SKIPPED: i64 = -32007;
18pub const JSON_RPC_SERVER_ERROR_NO_SNAPSHOT: i64 = -32008;
19pub const JSON_RPC_SERVER_ERROR_LONG_TERM_STORAGE_SLOT_SKIPPED: i64 = -32009;
20pub const JSON_RPC_SERVER_ERROR_KEY_EXCLUDED_FROM_SECONDARY_INDEX: i64 = -32010;
21pub const JSON_RPC_SERVER_ERROR_TRANSACTION_HISTORY_NOT_AVAILABLE: i64 = -32011;
22pub const JSON_RPC_SCAN_ERROR: i64 = -32012;
23pub const JSON_RPC_SERVER_ERROR_TRANSACTION_SIGNATURE_LEN_MISMATCH: i64 = -32013;
24pub const JSON_RPC_SERVER_ERROR_BLOCK_STATUS_NOT_AVAILABLE_YET: i64 = -32014;
25pub const JSON_RPC_SERVER_ERROR_UNSUPPORTED_TRANSACTION_VERSION: i64 = -32015;
26pub const JSON_RPC_SERVER_ERROR_MIN_CONTEXT_SLOT_NOT_REACHED: i64 = -32016;
27
28#[derive(Error, Debug)]
29pub enum RpcCustomError {
30    #[error("BlockCleanedUp")]
31    BlockCleanedUp {
32        slot: Slot,
33        first_available_block: Slot,
34    },
35    #[error("SendTransactionPreflightFailure")]
36    SendTransactionPreflightFailure {
37        message: String,
38        result: RpcSimulateTransactionResult,
39    },
40    #[error("TransactionSignatureVerificationFailure")]
41    TransactionSignatureVerificationFailure,
42    #[error("BlockNotAvailable")]
43    BlockNotAvailable { slot: Slot },
44    #[error("NodeUnhealthy")]
45    NodeUnhealthy { num_slots_behind: Option<Slot> },
46    #[error("TransactionPrecompileVerificationFailure")]
47    TransactionPrecompileVerificationFailure(solana_sdk::transaction::TransactionError),
48    #[error("SlotSkipped")]
49    SlotSkipped { slot: Slot },
50    #[error("NoSnapshot")]
51    NoSnapshot,
52    #[error("LongTermStorageSlotSkipped")]
53    LongTermStorageSlotSkipped { slot: Slot },
54    #[error("KeyExcludedFromSecondaryIndex")]
55    KeyExcludedFromSecondaryIndex { index_key: String },
56    #[error("TransactionHistoryNotAvailable")]
57    TransactionHistoryNotAvailable,
58    #[error("ScanError")]
59    ScanError { message: String },
60    #[error("TransactionSignatureLenMismatch")]
61    TransactionSignatureLenMismatch,
62    #[error("BlockStatusNotAvailableYet")]
63    BlockStatusNotAvailableYet { slot: Slot },
64    #[error("UnsupportedTransactionVersion")]
65    UnsupportedTransactionVersion(u8),
66    #[error("MinContextSlotNotReached")]
67    MinContextSlotNotReached { context_slot: Slot },
68}
69
70#[derive(Debug, Serialize, Deserialize)]
71#[serde(rename_all = "camelCase")]
72pub struct NodeUnhealthyErrorData {
73    pub num_slots_behind: Option<Slot>,
74}
75
76#[derive(Debug, Serialize, Deserialize)]
77#[serde(rename_all = "camelCase")]
78pub struct MinContextSlotNotReachedErrorData {
79    pub context_slot: Slot,
80}
81
82impl From<EncodeError> for RpcCustomError {
83    fn from(err: EncodeError) -> Self {
84        match err {
85            EncodeError::UnsupportedTransactionVersion(version) => {
86                Self::UnsupportedTransactionVersion(version)
87            }
88        }
89    }
90}
91
92impl From<RpcCustomError> for Error {
93    fn from(e: RpcCustomError) -> Self {
94        match e {
95            RpcCustomError::BlockCleanedUp {
96                slot,
97                first_available_block,
98            } => Self {
99                code: ErrorCode::ServerError(JSON_RPC_SERVER_ERROR_BLOCK_CLEANED_UP),
100                message: format!(
101                    "Block {} cleaned up, does not exist on node. First available block: {}",
102                    slot, first_available_block,
103                ),
104                data: None,
105            },
106            RpcCustomError::SendTransactionPreflightFailure { message, result } => Self {
107                code: ErrorCode::ServerError(
108                    JSON_RPC_SERVER_ERROR_SEND_TRANSACTION_PREFLIGHT_FAILURE,
109                ),
110                message,
111                data: Some(serde_json::json!(result)),
112            },
113            RpcCustomError::TransactionSignatureVerificationFailure => Self {
114                code: ErrorCode::ServerError(
115                    JSON_RPC_SERVER_ERROR_TRANSACTION_SIGNATURE_VERIFICATION_FAILURE,
116                ),
117                message: "Transaction signature verification failure".to_string(),
118                data: None,
119            },
120            RpcCustomError::BlockNotAvailable { slot } => Self {
121                code: ErrorCode::ServerError(JSON_RPC_SERVER_ERROR_BLOCK_NOT_AVAILABLE),
122                message: format!("Block not available for slot {}", slot),
123                data: None,
124            },
125            RpcCustomError::NodeUnhealthy { num_slots_behind } => Self {
126                code: ErrorCode::ServerError(JSON_RPC_SERVER_ERROR_NODE_UNHEALTHY),
127                message: if let Some(num_slots_behind) = num_slots_behind {
128                    format!("Node is behind by {} slots", num_slots_behind)
129                } else {
130                    "Node is unhealthy".to_string()
131                },
132                data: Some(serde_json::json!(NodeUnhealthyErrorData {
133                    num_slots_behind
134                })),
135            },
136            RpcCustomError::TransactionPrecompileVerificationFailure(e) => Self {
137                code: ErrorCode::ServerError(
138                    JSON_RPC_SERVER_ERROR_TRANSACTION_SIGNATURE_VERIFICATION_FAILURE,
139                ),
140                message: format!("Transaction precompile verification failure {:?}", e),
141                data: None,
142            },
143            RpcCustomError::SlotSkipped { slot } => Self {
144                code: ErrorCode::ServerError(JSON_RPC_SERVER_ERROR_SLOT_SKIPPED),
145                message: format!(
146                    "Slot {} was skipped, or missing due to ledger jump to recent snapshot",
147                    slot
148                ),
149                data: None,
150            },
151            RpcCustomError::NoSnapshot => Self {
152                code: ErrorCode::ServerError(JSON_RPC_SERVER_ERROR_NO_SNAPSHOT),
153                message: "No snapshot".to_string(),
154                data: None,
155            },
156            RpcCustomError::LongTermStorageSlotSkipped { slot } => Self {
157                code: ErrorCode::ServerError(JSON_RPC_SERVER_ERROR_LONG_TERM_STORAGE_SLOT_SKIPPED),
158                message: format!("Slot {} was skipped, or missing in long-term storage", slot),
159                data: None,
160            },
161            RpcCustomError::KeyExcludedFromSecondaryIndex { index_key } => Self {
162                code: ErrorCode::ServerError(
163                    JSON_RPC_SERVER_ERROR_KEY_EXCLUDED_FROM_SECONDARY_INDEX,
164                ),
165                message: format!(
166                    "{} excluded from account secondary indexes; \
167                    this RPC method unavailable for key",
168                    index_key
169                ),
170                data: None,
171            },
172            RpcCustomError::TransactionHistoryNotAvailable => Self {
173                code: ErrorCode::ServerError(
174                    JSON_RPC_SERVER_ERROR_TRANSACTION_HISTORY_NOT_AVAILABLE,
175                ),
176                message: "Transaction history is not available from this node".to_string(),
177                data: None,
178            },
179            RpcCustomError::ScanError { message } => Self {
180                code: ErrorCode::ServerError(JSON_RPC_SCAN_ERROR),
181                message,
182                data: None,
183            },
184            RpcCustomError::TransactionSignatureLenMismatch => Self {
185                code: ErrorCode::ServerError(
186                    JSON_RPC_SERVER_ERROR_TRANSACTION_SIGNATURE_LEN_MISMATCH,
187                ),
188                message: "Transaction signature length mismatch".to_string(),
189                data: None,
190            },
191            RpcCustomError::BlockStatusNotAvailableYet { slot } => Self {
192                code: ErrorCode::ServerError(JSON_RPC_SERVER_ERROR_BLOCK_STATUS_NOT_AVAILABLE_YET),
193                message: format!("Block status not yet available for slot {}", slot),
194                data: None,
195            },
196            RpcCustomError::UnsupportedTransactionVersion(version) => Self {
197                code: ErrorCode::ServerError(JSON_RPC_SERVER_ERROR_UNSUPPORTED_TRANSACTION_VERSION),
198                message: format!(
199                    "Transaction version ({0}) is not supported by the requesting client. \
200                    Please try the request again with the following configuration parameter: \
201                    \"maxSupportedTransactionVersion\": {0}",
202                    version
203                ),
204                data: None,
205            },
206            RpcCustomError::MinContextSlotNotReached { context_slot } => Self {
207                code: ErrorCode::ServerError(JSON_RPC_SERVER_ERROR_MIN_CONTEXT_SLOT_NOT_REACHED),
208                message: "Minimum context slot has not been reached".to_string(),
209                data: Some(serde_json::json!(MinContextSlotNotReachedErrorData {
210                    context_slot,
211                })),
212            },
213        }
214    }
215}