1use futures::future::BoxFuture;
2use jsonrpsee::MethodResponse;
3use jsonrpsee::ResponsePayload;
4use jsonrpsee::core::middleware::ResponseFuture;
5use jsonrpsee::types::ErrorObjectOwned;
6use jsonrpsee::types::Id;
7use revm::context::DBErrorMarker;
8use revm::context::result::EVMError;
9use stratus_macros::ErrorCode;
10
11use super::execution_result::RevertReason;
12use crate::alias::JsonValue;
13use crate::eth::executor::EvmInput;
14use crate::eth::primitives::Address;
15use crate::eth::primitives::BlockFilter;
16use crate::eth::primitives::BlockNumber;
17use crate::eth::primitives::Bytes;
18use crate::eth::primitives::Nonce;
19use crate::ext::to_json_value;
20
21pub trait ErrorCode {
22 fn error_code(&self) -> i32;
23 fn str_repr_from_err_code(code: i32) -> Option<&'static str>;
24}
25
26#[derive(Debug, thiserror::Error, strum::EnumProperty, strum::IntoStaticStr, ErrorCode)]
27#[major_error_code = 1000]
28pub enum RpcError {
29 #[error("block filter does not point to a valid block.")]
30 #[error_code = 1]
31 BlockFilterInvalid { filter: BlockFilter },
32
33 #[error("denied because will fetch data from {actual} blocks, but the max allowed is {max}.")]
34 #[error_code = 2]
35 BlockRangeInvalid { actual: u64, max: u64 },
36
37 #[error("denied because client did not identify itself.")]
38 #[error_code = 3]
39 ClientMissing,
40
41 #[error("failed to decode {rust_type} parameter.")]
42 #[error_code = 4]
43 ParameterDecodeError { rust_type: &'static str, decode_error: String },
44
45 #[error("expected {rust_type} parameter, but received nothing.")]
46 #[error_code = 5]
47 ParameterMissing { rust_type: &'static str },
48
49 #[error("invalid subscription event: {event}")]
50 #[error_code = 6]
51 SubscriptionInvalid { event: String },
52
53 #[error("denied because reached maximum subscription limit of {max}.")]
54 #[error_code = 7]
55 SubscriptionLimit { max: u32 },
56
57 #[error("failed to decode transaction RLP data.")]
58 #[error_code = 8]
59 TransactionInvalid { decode_error: String },
60
61 #[error("miner mode param is invalid.")]
62 #[error_code = 9]
63 MinerModeParamInvalid,
64
65 #[error("parameter is invalid")]
66 #[error_code = 10]
67 ParameterInvalid,
68}
69
70#[derive(Debug, thiserror::Error, strum::EnumProperty, strum::IntoStaticStr, ErrorCode)]
71#[major_error_code = 2000]
72pub enum TransactionError {
73 #[error("function selector was not recognized: account at {address} is not a contract.")]
74 #[error_code = 1]
75 AccountNotContract { address: Address },
76
77 #[error("transaction nonce {transaction} does not match account nonce {account}.")]
78 #[error_code = 2]
79 Nonce { transaction: Nonce, account: Nonce },
80
81 #[error("EVM execution error: {0:?}.")]
82 #[error_code = 3]
83 EvmFailed(String), #[error("failed to execute transaction in leader: {0:?}.")]
86 #[error_code = 4]
87 LeaderFailed(ErrorObjectOwned),
88
89 #[error("failed to forward transaction to leader node.")]
90 #[error_code = 5]
91 ForwardToLeaderFailed,
92
93 #[error("transaction reverted during execution. output: {output}")]
94 #[error_code = 6]
95 RevertedCall { output: Bytes },
96
97 #[error("transaction from zero address is not allowed.")]
98 #[error_code = 7]
99 FromZeroAddress,
100
101 #[error("transaction reverted during execution. reason: {reason}")]
102 #[error_code = 8]
103 RevertedCallWithReason { reason: RevertReason },
104}
105
106#[derive(Debug, thiserror::Error, strum::EnumProperty, strum::IntoStaticStr, ErrorCode)]
107#[major_error_code = 3000]
108pub enum StorageError {
109 #[error("block conflict: {number} already exists in the permanent storage.")]
110 #[error_code = 1]
111 BlockConflict { number: BlockNumber },
112
113 #[error("mined number conflict between new block number ({new}) and mined block number ({mined}).")]
114 #[error_code = 2]
115 MinedNumberConflict { new: BlockNumber, mined: BlockNumber },
116
117 #[error("transaction input does not match block header")]
122 #[error_code = 4]
123 EvmInputMismatch { expected: Box<EvmInput>, actual: Box<EvmInput> },
124
125 #[error("pending number conflict between new block number ({new}) and pending block number ({pending}).")]
126 #[error_code = 5]
127 PendingNumberConflict { new: BlockNumber, pending: BlockNumber },
128
129 #[error("there are ({pending_txs}) pending transactions.")]
130 #[error_code = 6]
131 PendingTransactionsExist { pending_txs: usize },
132
133 #[error("rocksdb returned an error: {err}")]
134 #[error_code = 7]
135 RocksError { err: anyhow::Error },
136
137 #[error("block not found using filter: {filter}")]
138 #[error_code = 8]
139 BlockNotFound { filter: BlockFilter },
140
141 #[error("unexpected storage error: {msg}")]
142 #[error_code = 9]
143 Unexpected { msg: String },
144}
145
146#[derive(Debug, thiserror::Error, strum::EnumProperty, strum::IntoStaticStr, ErrorCode)]
147#[major_error_code = 4000]
148pub enum ImporterError {
149 #[error("importer is already running.")]
150 #[error_code = 1]
151 AlreadyRunning,
152
153 #[error("importer is already shutdown.")]
154 #[error_code = 2]
155 AlreadyShutdown,
156
157 #[error("failed to parse importer configuration.")]
158 #[error_code = 3]
159 ConfigParseError,
160
161 #[error("failed to initialize importer.")]
162 #[error_code = 4]
163 InitError,
164}
165
166#[derive(Debug, thiserror::Error, strum::EnumProperty, strum::IntoStaticStr, ErrorCode)]
167#[major_error_code = 5000]
168pub enum ConsensusError {
169 #[error("consensus is temporarily unavailable for follower node.")]
170 #[error_code = 1]
171 Unavailable,
172
173 #[error("consensus is set.")]
174 #[error_code = 2]
175 Set,
176
177 #[error("failed to update consensus: Consensus is not set.")]
178 #[error_code = 3]
179 NotSet,
180}
181
182#[derive(Debug, thiserror::Error, strum::EnumProperty, strum::IntoStaticStr, ErrorCode)]
183#[major_error_code = 6000]
184pub enum UnexpectedError {
185 #[error("unexpected channel {channel} closed.")]
186 #[error_code = 1]
187 ChannelClosed { channel: &'static str },
188
189 #[error("unexpected error: {0:?}.")]
190 #[error_code = 2]
191 Unexpected(anyhow::Error),
192}
193
194#[derive(Debug, thiserror::Error, strum::EnumProperty, strum::IntoStaticStr, ErrorCode)]
195#[major_error_code = 7000]
196pub enum StateError {
197 #[error("stratus is not ready to start servicing requests.")]
198 #[error_code = 1]
199 StratusNotReady,
200
201 #[error("stratus is shutting down.")]
202 #[error_code = 2]
203 StratusShutdown,
204
205 #[error("stratus node is not a follower.")]
206 #[error_code = 3]
207 StratusNotFollower,
208
209 #[error("incorrect password, cancelling operation.")]
210 #[error_code = 4]
211 InvalidPassword,
212
213 #[error("stratus node is already in the process of changing mode.")]
214 #[error_code = 5]
215 ModeChangeInProgress,
216
217 #[error("transaction processing is temporarily disabled.")]
218 #[error_code = 6]
219 TransactionsDisabled,
220
221 #[error("can't change miner mode while transactions are enabled.")]
222 #[error_code = 7]
223 TransactionsEnabled,
224}
225
226#[derive(Debug, thiserror::Error, strum::EnumProperty, strum::IntoStaticStr)]
227pub enum StratusError {
228 #[error(transparent)]
229 RPC(#[from] RpcError),
230
231 #[error(transparent)]
232 Transaction(#[from] TransactionError),
233
234 #[error(transparent)]
235 Storage(#[from] StorageError),
236
237 #[error(transparent)]
238 Importer(#[from] ImporterError),
239
240 #[error(transparent)]
241 Consensus(#[from] ConsensusError),
242
243 #[error(transparent)]
244 Unexpected(#[from] UnexpectedError),
245
246 #[error(transparent)]
247 State(#[from] StateError),
248}
249
250impl ErrorCode for StratusError {
251 fn error_code(&self) -> i32 {
252 match self {
253 Self::RPC(err) => err.error_code(),
254 Self::Transaction(err) => err.error_code(),
255 Self::Storage(err) => err.error_code(),
256 Self::Importer(err) => err.error_code(),
257 Self::Consensus(err) => err.error_code(),
258 Self::Unexpected(err) => err.error_code(),
259 Self::State(err) => err.error_code(),
260 }
261 }
262
263 fn str_repr_from_err_code(code: i32) -> Option<&'static str> {
264 let major = code / 1000;
265 match major {
266 1 => RpcError::str_repr_from_err_code(code),
267 2 => TransactionError::str_repr_from_err_code(code),
268 3 => StorageError::str_repr_from_err_code(code),
269 4 => ImporterError::str_repr_from_err_code(code),
270 5 => ConsensusError::str_repr_from_err_code(code),
271 6 => UnexpectedError::str_repr_from_err_code(code),
272 7 => StateError::str_repr_from_err_code(code),
273 _ => None,
274 }
275 }
276}
277
278impl DBErrorMarker for StratusError {}
279
280impl StratusError {
281 pub fn rpc_message(&self) -> String {
283 self.to_string()
284 }
285
286 pub fn rpc_data(&self) -> JsonValue {
288 match self {
289 Self::RPC(RpcError::BlockFilterInvalid { filter }) => to_json_value(filter),
291 Self::RPC(RpcError::ParameterDecodeError { decode_error, .. }) => to_json_value(decode_error),
292
293 Self::RPC(RpcError::TransactionInvalid { decode_error }) => to_json_value(decode_error),
295 Self::Transaction(TransactionError::EvmFailed(e)) => JsonValue::String(e.to_string()),
296 Self::Transaction(TransactionError::RevertedCall { output }) => to_json_value(output),
297 Self::Transaction(TransactionError::RevertedCallWithReason { reason }) => to_json_value(reason),
298
299 Self::Unexpected(UnexpectedError::Unexpected(e)) => JsonValue::String(e.to_string()),
301
302 _ => JsonValue::Null,
303 }
304 }
305
306 pub fn to_response_future<'a>(self, id: Id<'_>) -> ResponseFuture<BoxFuture<'a, MethodResponse>, MethodResponse> {
307 let response = ResponsePayload::<()>::error(StratusError::RPC(RpcError::ClientMissing));
308 let method_response = MethodResponse::response(id, response, u32::MAX as usize);
309 ResponseFuture::ready(method_response)
310 }
311}
312
313impl From<anyhow::Error> for StratusError {
318 fn from(value: anyhow::Error) -> Self {
319 Self::Unexpected(UnexpectedError::Unexpected(value))
320 }
321}
322
323impl From<serde_json::Error> for StratusError {
324 fn from(value: serde_json::Error) -> Self {
325 Self::Unexpected(UnexpectedError::Unexpected(anyhow::anyhow!(value)))
326 }
327}
328
329impl From<EVMError<StratusError>> for StratusError {
330 fn from(value: EVMError<StratusError>) -> Self {
331 match value {
332 EVMError::Database(err) => err,
333 EVMError::Custom(err) => Self::Transaction(TransactionError::EvmFailed(err)),
334 EVMError::Header(err) => Self::Transaction(TransactionError::EvmFailed(err.to_string())),
335 EVMError::Transaction(err) => Self::Transaction(TransactionError::EvmFailed(err.to_string())),
336 }
337 }
338}
339
340impl From<StratusError> for ErrorObjectOwned {
344 fn from(value: StratusError) -> Self {
345 if let StratusError::Transaction(TransactionError::LeaderFailed(response)) = value {
347 return response;
348 }
349 let data = match value.rpc_data() {
351 serde_json::Value::String(data_str) => {
352 let data_str = data_str.trim_start_matches('\"').trim_end_matches('\"').replace("\\\"", "\"");
353 JsonValue::String(data_str)
354 }
355 data => data,
356 };
357
358 Self::owned(value.error_code(), value.rpc_message(), Some(data))
359 }
360}
361
362#[derive(Debug, thiserror::Error)]
363pub enum DecodeInputError {
364 #[error("Input too short: {message}")]
365 InputTooShort { message: String },
366
367 #[error("Function unknown: {message}")]
368 FunctionUnknown { message: String },
369
370 #[error("Invalid input: {message}")]
371 InvalidAbi { message: String },
372
373 #[error("Invalid ABI: {source}")]
374 InvalidInput {
375 #[from]
376 source: ethabi::Error,
377 },
378}
379
380#[cfg(test)]
381mod tests {
382 use super::*;
383
384 #[test]
385 fn test_str_repr_from_err_code() {
386 assert_eq!(StratusError::str_repr_from_err_code(1001), Some("BlockFilterInvalid"));
388
389 assert_eq!(StratusError::str_repr_from_err_code(2001), Some("AccountNotContract"));
391
392 assert_eq!(StratusError::str_repr_from_err_code(3001), Some("BlockConflict"));
394
395 assert_eq!(StratusError::str_repr_from_err_code(4001), Some("AlreadyRunning"));
397
398 assert_eq!(StratusError::str_repr_from_err_code(5001), Some("Unavailable"));
400
401 assert_eq!(StratusError::str_repr_from_err_code(6001), Some("ChannelClosed"));
403
404 assert_eq!(StratusError::str_repr_from_err_code(7003), Some("StratusNotFollower"));
406
407 assert_eq!(StratusError::str_repr_from_err_code(9999), None);
409 }
410}