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