gemachain_client/
rpc_custom_error.rs

1//! Implementation defined RPC server errors
2use thiserror::Error;
3use {
4    crate::rpc_response::RpcSimulateTransactionResult,
5    jsonrpc_core::{Error, ErrorCode},
6    gemachain_sdk::clock::Slot,
7};
8
9pub const JSON_RPC_SERVER_ERROR_BLOCK_CLEANED_UP: i64 = -32001;
10pub const JSON_RPC_SERVER_ERROR_SEND_TRANSACTION_PREFLIGHT_FAILURE: i64 = -32002;
11pub const JSON_RPC_SERVER_ERROR_TRANSACTION_SIGNATURE_VERIFICATION_FAILURE: i64 = -32003;
12pub const JSON_RPC_SERVER_ERROR_BLOCK_NOT_AVAILABLE: i64 = -32004;
13pub const JSON_RPC_SERVER_ERROR_NODE_UNHEALTHY: i64 = -32005;
14pub const JSON_RPC_SERVER_ERROR_TRANSACTION_PRECOMPILE_VERIFICATION_FAILURE: i64 = -32006;
15pub const JSON_RPC_SERVER_ERROR_SLOT_SKIPPED: i64 = -32007;
16pub const JSON_RPC_SERVER_ERROR_NO_SNAPSHOT: i64 = -32008;
17pub const JSON_RPC_SERVER_ERROR_LONG_TERM_STORAGE_SLOT_SKIPPED: i64 = -32009;
18pub const JSON_RPC_SERVER_ERROR_KEY_EXCLUDED_FROM_SECONDARY_INDEX: i64 = -32010;
19pub const JSON_RPC_SERVER_ERROR_TRANSACTION_HISTORY_NOT_AVAILABLE: i64 = -32011;
20pub const JSON_RPC_SCAN_ERROR: i64 = -32012;
21pub const JSON_RPC_SERVER_ERROR_TRANSACTION_SIGNATURE_LEN_MISMATCH: i64 = -32013;
22
23#[derive(Error, Debug)]
24pub enum RpcCustomError {
25    #[error("BlockCleanedUp")]
26    BlockCleanedUp {
27        slot: Slot,
28        first_available_block: Slot,
29    },
30    #[error("SendTransactionPreflightFailure")]
31    SendTransactionPreflightFailure {
32        message: String,
33        result: RpcSimulateTransactionResult,
34    },
35    #[error("TransactionSignatureVerificationFailure")]
36    TransactionSignatureVerificationFailure,
37    #[error("BlockNotAvailable")]
38    BlockNotAvailable { slot: Slot },
39    #[error("NodeUnhealthy")]
40    NodeUnhealthy { num_slots_behind: Option<Slot> },
41    #[error("TransactionPrecompileVerificationFailure")]
42    TransactionPrecompileVerificationFailure(gemachain_sdk::transaction::TransactionError),
43    #[error("SlotSkipped")]
44    SlotSkipped { slot: Slot },
45    #[error("NoSnapshot")]
46    NoSnapshot,
47    #[error("LongTermStorageSlotSkipped")]
48    LongTermStorageSlotSkipped { slot: Slot },
49    #[error("KeyExcludedFromSecondaryIndex")]
50    KeyExcludedFromSecondaryIndex { index_key: String },
51    #[error("TransactionHistoryNotAvailable")]
52    TransactionHistoryNotAvailable,
53    #[error("ScanError")]
54    ScanError { message: String },
55    #[error("TransactionSignatureLenMismatch")]
56    TransactionSignatureLenMismatch,
57}
58
59#[derive(Debug, Serialize, Deserialize)]
60#[serde(rename_all = "camelCase")]
61pub struct NodeUnhealthyErrorData {
62    pub num_slots_behind: Option<Slot>,
63}
64
65impl From<RpcCustomError> for Error {
66    fn from(e: RpcCustomError) -> Self {
67        match e {
68            RpcCustomError::BlockCleanedUp {
69                slot,
70                first_available_block,
71            } => Self {
72                code: ErrorCode::ServerError(JSON_RPC_SERVER_ERROR_BLOCK_CLEANED_UP),
73                message: format!(
74                    "Block {} cleaned up, does not exist on node. First available block: {}",
75                    slot, first_available_block,
76                ),
77                data: None,
78            },
79            RpcCustomError::SendTransactionPreflightFailure { message, result } => Self {
80                code: ErrorCode::ServerError(
81                    JSON_RPC_SERVER_ERROR_SEND_TRANSACTION_PREFLIGHT_FAILURE,
82                ),
83                message,
84                data: Some(serde_json::json!(result)),
85            },
86            RpcCustomError::TransactionSignatureVerificationFailure => Self {
87                code: ErrorCode::ServerError(
88                    JSON_RPC_SERVER_ERROR_TRANSACTION_SIGNATURE_VERIFICATION_FAILURE,
89                ),
90                message: "Transaction signature verification failure".to_string(),
91                data: None,
92            },
93            RpcCustomError::BlockNotAvailable { slot } => Self {
94                code: ErrorCode::ServerError(JSON_RPC_SERVER_ERROR_BLOCK_NOT_AVAILABLE),
95                message: format!("Block not available for slot {}", slot),
96                data: None,
97            },
98            RpcCustomError::NodeUnhealthy { num_slots_behind } => Self {
99                code: ErrorCode::ServerError(JSON_RPC_SERVER_ERROR_NODE_UNHEALTHY),
100                message: if let Some(num_slots_behind) = num_slots_behind {
101                    format!("Node is behind by {} slots", num_slots_behind)
102                } else {
103                    "Node is unhealthy".to_string()
104                },
105                data: Some(serde_json::json!(NodeUnhealthyErrorData {
106                    num_slots_behind
107                })),
108            },
109            RpcCustomError::TransactionPrecompileVerificationFailure(e) => Self {
110                code: ErrorCode::ServerError(
111                    JSON_RPC_SERVER_ERROR_TRANSACTION_SIGNATURE_VERIFICATION_FAILURE,
112                ),
113                message: format!("Transaction precompile verification failure {:?}", e),
114                data: None,
115            },
116            RpcCustomError::SlotSkipped { slot } => Self {
117                code: ErrorCode::ServerError(JSON_RPC_SERVER_ERROR_SLOT_SKIPPED),
118                message: format!(
119                    "Slot {} was skipped, or missing due to ledger jump to recent snapshot",
120                    slot
121                ),
122                data: None,
123            },
124            RpcCustomError::NoSnapshot => Self {
125                code: ErrorCode::ServerError(JSON_RPC_SERVER_ERROR_NO_SNAPSHOT),
126                message: "No snapshot".to_string(),
127                data: None,
128            },
129            RpcCustomError::LongTermStorageSlotSkipped { slot } => Self {
130                code: ErrorCode::ServerError(JSON_RPC_SERVER_ERROR_LONG_TERM_STORAGE_SLOT_SKIPPED),
131                message: format!("Slot {} was skipped, or missing in long-term storage", slot),
132                data: None,
133            },
134            RpcCustomError::KeyExcludedFromSecondaryIndex { index_key } => Self {
135                code: ErrorCode::ServerError(
136                    JSON_RPC_SERVER_ERROR_KEY_EXCLUDED_FROM_SECONDARY_INDEX,
137                ),
138                message: format!(
139                    "{} excluded from account secondary indexes; \
140                    this RPC method unavailable for key",
141                    index_key
142                ),
143                data: None,
144            },
145            RpcCustomError::TransactionHistoryNotAvailable => Self {
146                code: ErrorCode::ServerError(
147                    JSON_RPC_SERVER_ERROR_TRANSACTION_HISTORY_NOT_AVAILABLE,
148                ),
149                message: "Transaction history is not available from this node".to_string(),
150                data: None,
151            },
152            RpcCustomError::ScanError { message } => Self {
153                code: ErrorCode::ServerError(JSON_RPC_SCAN_ERROR),
154                message,
155                data: None,
156            },
157            RpcCustomError::TransactionSignatureLenMismatch => Self {
158                code: ErrorCode::ServerError(
159                    JSON_RPC_SERVER_ERROR_TRANSACTION_SIGNATURE_LEN_MISMATCH,
160                ),
161                message: "Transaction signature length mismatch".to_string(),
162                data: None,
163            },
164        }
165    }
166}