iota_indexer/
errors.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2024 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5use fastcrypto::error::FastCryptoError;
6use iota_data_ingestion_core::IngestionError;
7use iota_json_rpc_api::{error_object_from_rpc, internal_error};
8use iota_names::error::IotaNamesError;
9use iota_types::{
10    base_types::ObjectIDParseError,
11    error::{IotaError, IotaObjectResponseError, UserInputError},
12};
13use jsonrpsee::{core::ClientError as RpcError, types::ErrorObjectOwned};
14use thiserror::Error;
15
16#[derive(Debug, Error)]
17pub struct DataDownloadError {
18    pub error: IndexerError,
19    pub next_checkpoint_sequence_number: u64,
20}
21
22impl std::fmt::Display for DataDownloadError {
23    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
24        write!(
25            f,
26            "next_checkpoint_seq: {}, error: {}",
27            self.next_checkpoint_sequence_number, self.error
28        )
29    }
30}
31
32#[derive(Debug, Error)]
33#[non_exhaustive]
34pub enum IndexerError {
35    #[error("Stream closed unexpectedly with error: `{0}`")]
36    ChannelClosed(String),
37
38    #[error("Indexer failed to convert timestamp to DateTime with error: `{0}`")]
39    DateTimeParsing(String),
40
41    #[error("Indexer failed to deserialize event from events table with error: `{0}`")]
42    EventDeserialization(String),
43
44    #[error(
45        "Fullnode returns unexpected responses, which may block indexers from proceeding, with error: `{0}`"
46    )]
47    UnexpectedFullnodeResponse(String),
48
49    #[error("Indexer failed to transform data with error: `{0}`")]
50    DataTransformation(String),
51
52    #[error("Indexer failed to read fullnode with error: `{0}`")]
53    FullNodeReading(String),
54
55    #[error("Indexer failed to convert structs to diesel Insertable with error: `{0}`")]
56    InsertableParsing(String),
57
58    #[error("Indexer failed to build JsonRpcServer with error: `{0}`")]
59    JsonRpcServer(#[from] iota_json_rpc::error::Error),
60
61    #[error("Indexer failed to find object mutations, which should never happen.")]
62    ObjectMutationNotAvailable,
63
64    #[error("Indexer failed to build PG connection pool with error: `{0}`")]
65    PgConnectionPoolInit(String),
66
67    #[error("Indexer failed to get a pool connection from PG connection pool with error: `{0}`")]
68    PgPoolConnection(String),
69
70    #[error("Indexer failed to read PostgresDB with error: `{0}`")]
71    PostgresRead(String),
72
73    #[error("Indexer failed to reset PostgresDB with error: `{0}`")]
74    PostgresReset(String),
75
76    #[error("Indexer failed to commit changes to PostgresDB with error: `{0}`")]
77    PostgresWrite(String),
78
79    #[error(transparent)]
80    Postgres(#[from] diesel::result::Error),
81
82    #[error("Indexer failed to initialize fullnode Http client with error: `{0}`")]
83    HttpClientInit(String),
84
85    #[error("Indexer failed to serialize/deserialize with error: `{0}`")]
86    Serde(String),
87
88    #[error("Indexer error related to dynamic field: `{0}`")]
89    DynamicField(String),
90
91    #[error("Indexer does not support the feature with error: `{0}`")]
92    NotSupported(String),
93
94    #[error("Indexer read corrupted/incompatible data from persistent storage: `{0}`")]
95    PersistentStorageDataCorruption(String),
96
97    #[error("Indexer generic error: `{0}`")]
98    Generic(String),
99
100    #[error("Indexer failed to resolve object to move struct with error: `{0}`")]
101    ResolveMoveStruct(String),
102
103    #[error(transparent)]
104    Uncategorized(#[from] anyhow::Error),
105
106    #[error(transparent)]
107    ObjectIdParse(#[from] ObjectIDParseError),
108
109    #[error("Invalid transaction digest with error: `{0}`")]
110    InvalidTransactionDigest(String),
111
112    #[error(transparent)]
113    Iota(#[from] IotaError),
114
115    #[error(transparent)]
116    Bcs(#[from] bcs::Error),
117
118    #[error("Invalid argument with error: `{0}`")]
119    InvalidArgument(String),
120
121    #[error(transparent)]
122    UserInput(#[from] UserInputError),
123
124    #[error("Indexer failed to resolve module with error: `{0}`")]
125    ModuleResolution(String),
126
127    #[error(transparent)]
128    ObjectResponse(#[from] IotaObjectResponseError),
129
130    #[error(transparent)]
131    FastCrypto(#[from] FastCryptoError),
132
133    #[error("`{0}`: `{1}`")]
134    ErrorWithContext(String, Box<IndexerError>),
135
136    #[error("Indexer failed to send item to channel with error: `{0}`")]
137    MpscChannel(String),
138
139    #[error("Failed to process checkpoint(s): `{0}`")]
140    CheckpointProcessing(String),
141
142    #[error(transparent)]
143    Ingestion(#[from] IngestionError),
144
145    #[error(transparent)]
146    IotaNames(#[from] IotaNamesError),
147}
148
149pub trait Context<T> {
150    fn context(self, context: &str) -> Result<T, IndexerError>;
151}
152
153impl<T> Context<T> for Result<T, IndexerError> {
154    fn context(self, context: &str) -> Result<T, IndexerError> {
155        self.map_err(|e| IndexerError::ErrorWithContext(context.to_string(), Box::new(e)))
156    }
157}
158
159impl From<IndexerError> for RpcError {
160    fn from(e: IndexerError) -> Self {
161        RpcError::Call(internal_error(e))
162    }
163}
164
165impl From<IndexerError> for ErrorObjectOwned {
166    fn from(value: IndexerError) -> Self {
167        error_object_from_rpc(value.into())
168    }
169}
170
171impl From<tokio::task::JoinError> for IndexerError {
172    fn from(value: tokio::task::JoinError) -> Self {
173        IndexerError::Uncategorized(anyhow::Error::from(value))
174    }
175}