1use {
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
10pub 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}