1use axum::http::StatusCode;
6
7pub type Result<T, E = RestError> = std::result::Result<T, E>;
8
9pub struct RestError {
10 status: StatusCode,
11 message: Option<String>,
12}
13
14impl RestError {
15 pub fn new<T: Into<String>>(status: StatusCode, message: T) -> Self {
16 Self {
17 status,
18 message: Some(message.into()),
19 }
20 }
21
22 pub fn not_found() -> Self {
23 Self {
24 status: StatusCode::NOT_FOUND,
25 message: None,
26 }
27 }
28}
29
30impl axum::response::IntoResponse for RestError {
32 fn into_response(self) -> axum::response::Response {
33 match self.message {
34 Some(message) => (self.status, message).into_response(),
35 None => self.status.into_response(),
36 }
37 }
38}
39
40impl From<iota_types::storage::error::Error> for RestError {
41 fn from(value: iota_types::storage::error::Error) -> Self {
42 Self {
43 status: StatusCode::INTERNAL_SERVER_ERROR,
44 message: Some(value.to_string()),
45 }
46 }
47}
48
49impl From<anyhow::Error> for RestError {
50 fn from(value: anyhow::Error) -> Self {
51 Self {
52 status: StatusCode::INTERNAL_SERVER_ERROR,
53 message: Some(value.to_string()),
54 }
55 }
56}
57
58impl From<iota_types::iota_sdk_types_conversions::SdkTypeConversionError> for RestError {
59 fn from(value: iota_types::iota_sdk_types_conversions::SdkTypeConversionError) -> Self {
60 Self {
61 status: StatusCode::INTERNAL_SERVER_ERROR,
62 message: Some(value.to_string()),
63 }
64 }
65}
66
67impl From<bcs::Error> for RestError {
68 fn from(value: bcs::Error) -> Self {
69 Self {
70 status: StatusCode::INTERNAL_SERVER_ERROR,
71 message: Some(value.to_string()),
72 }
73 }
74}
75
76impl From<iota_types::quorum_driver_types::QuorumDriverError> for RestError {
77 fn from(error: iota_types::quorum_driver_types::QuorumDriverError) -> Self {
78 use iota_types::{error::IotaError, quorum_driver_types::QuorumDriverError::*};
79 use itertools::Itertools;
80
81 match error {
82 InvalidUserSignature(err) => {
83 let message = {
84 let err = match err {
85 IotaError::UserInput { error } => error.to_string(),
86 _ => err.to_string(),
87 };
88 format!("Invalid user signature: {err}")
89 };
90
91 RestError::new(StatusCode::BAD_REQUEST, message)
92 }
93 QuorumDriverInternal(err) => {
94 RestError::new(StatusCode::INTERNAL_SERVER_ERROR, err.to_string())
95 }
96 ObjectsDoubleUsed { conflicting_txes } => {
97 let new_map = conflicting_txes
98 .into_iter()
99 .map(|(digest, (pairs, _))| {
100 (
101 digest,
102 pairs.into_iter().map(|(_, obj_ref)| obj_ref).collect(),
103 )
104 })
105 .collect::<std::collections::BTreeMap<_, Vec<_>>>();
106
107 let message = format!(
108 "Failed to sign transaction by a quorum of validators because of locked objects. Conflicting Transactions:\n{new_map:#?}",
109 );
110
111 RestError::new(StatusCode::CONFLICT, message)
112 }
113 TimeoutBeforeFinality | FailedWithTransientErrorAfterMaximumAttempts { .. } => {
114 RestError::new(
116 StatusCode::SERVICE_UNAVAILABLE,
117 "timed-out before finality could be reached",
118 )
119 }
120 NonRecoverableTransactionError { errors } => {
121 let new_errors: Vec<String> = errors
122 .into_iter()
123 .sorted_by(|(_, a, _), (_, b, _)| b.cmp(a))
125 .filter_map(|(err, _, _)| {
126 match &err {
127 IotaError::UserInput { error } => Some(error.to_string()),
138 _ => {
139 if err.is_retryable().0 {
140 None
141 } else {
142 Some(err.to_string())
143 }
144 }
145 }
146 })
147 .collect();
148
149 assert!(
150 !new_errors.is_empty(),
151 "NonRecoverableTransactionError should have at least one non-retryable error"
152 );
153
154 let error_list = new_errors.join(", ");
155 let error_msg = format!(
156 "Transaction execution failed due to issues with transaction inputs, please review the errors and try again: {error_list}."
157 );
158
159 RestError::new(StatusCode::BAD_REQUEST, error_msg)
160 }
161 TxAlreadyFinalizedWithDifferentUserSignatures => RestError::new(
162 StatusCode::CONFLICT,
163 "The transaction is already finalized but with different user signatures",
164 ),
165 SystemOverload { .. } | SystemOverloadRetryAfter { .. } => {
166 RestError::new(StatusCode::SERVICE_UNAVAILABLE, "system is overloaded")
168 }
169 }
170 }
171}