photon_indexer/api/
error.rs

1use crate::common::typedefs::hash::ParseHashError;
2use crate::metric;
3use cadence_macros::statsd_count;
4use jsonrpsee::core::Error as RpcError;
5use jsonrpsee::types::error::CallError;
6use log::error;
7use solana_sdk::pubkey::ParsePubkeyError;
8use thiserror::Error;
9
10#[derive(Error, Debug, PartialEq, Eq)]
11pub enum PhotonApiError {
12    #[error("Validation Error: {0}")]
13    ValidationError(String),
14    #[error("Invalid Public Key: field '{field}'")]
15    InvalidPubkey { field: String },
16    #[error("Database Error: {0}")]
17    DatabaseError(#[from] sea_orm::DbErr),
18    #[error("Record Not Found: {0}")]
19    RecordNotFound(String),
20    #[error("Unexpected Error: {0}")]
21    UnexpectedError(String),
22    #[error("Node is behind {0} slots")]
23    StaleSlot(u64),
24}
25
26// TODO: Simplify error conversions and ensure we adhere
27// to the proper RPC and HTTP codes.
28impl From<PhotonApiError> for RpcError {
29    fn from(val: PhotonApiError) -> Self {
30        match val {
31            PhotonApiError::ValidationError(_) => {
32                metric! {
33                    statsd_count!("validation_api_error", 1);
34                }
35                invalid_request(val)
36            }
37            PhotonApiError::InvalidPubkey { .. } => {
38                metric! {
39                    statsd_count!("invalid_pubkey_api_error", 1);
40                }
41                invalid_request(val)
42            }
43            PhotonApiError::RecordNotFound(_) => {
44                metric! {
45                    statsd_count!("record_not_found_api_error", 1);
46                }
47                invalid_request(val)
48            }
49            PhotonApiError::StaleSlot(_) => {
50                metric! {
51                    statsd_count!("stale_slot_api_error", 1);
52                }
53                invalid_request(val)
54            }
55            PhotonApiError::DatabaseError(e) => {
56                error!("Internal server database error: {}", e);
57                metric! {
58                    statsd_count!("internal_database_api_error", 1);
59                }
60                internal_server_error()
61            }
62            PhotonApiError::UnexpectedError(e) => {
63                error!("Internal server error: {}", e);
64                metric! {
65                    statsd_count!("unexpected_api_error", 1);
66                }
67                internal_server_error()
68            }
69        }
70    }
71}
72
73// The API contract receives parsed input from the user, so if we get a ParseHashError it means
74// that the database itself returned an invalid hash.
75impl From<ParseHashError> for PhotonApiError {
76    fn from(_error: ParseHashError) -> Self {
77        PhotonApiError::UnexpectedError("Invalid hash in database".to_string())
78    }
79}
80
81impl From<ParsePubkeyError> for PhotonApiError {
82    fn from(_error: ParsePubkeyError) -> Self {
83        PhotonApiError::UnexpectedError("Invalid public key in database".to_string())
84    }
85}
86
87fn invalid_request(e: PhotonApiError) -> RpcError {
88    RpcError::Call(CallError::from_std_error(e))
89}
90
91fn internal_server_error() -> RpcError {
92    RpcError::Call(CallError::Failed(anyhow::anyhow!("Internal server error")))
93}