1use std::collections::BTreeMap;
6
7use fastcrypto::error::FastCryptoError;
8use hyper::header::InvalidHeaderValue;
9use iota_json_rpc_api::{
10 TRANSACTION_EXECUTION_CLIENT_ERROR_CODE, TRANSIENT_ERROR_CODE, error_object_from_rpc,
11};
12use iota_names::error::IotaNamesError;
13use iota_types::{
14 error::{IotaError, IotaObjectResponseError, UserInputError},
15 quorum_driver_types::QuorumDriverError,
16};
17use itertools::Itertools;
18use jsonrpsee::{
19 core::{ClientError as RpcError, RegisterMethodError},
20 types::{
21 ErrorObject, ErrorObjectOwned,
22 error::{CALL_EXECUTION_FAILED_CODE, ErrorCode, INTERNAL_ERROR_CODE},
23 },
24};
25use thiserror::Error;
26use tokio::task::JoinError;
27
28use crate::authority_state::StateReadError;
29
30pub type RpcInterimResult<T = ()> = Result<T, Error>;
31
32#[derive(Debug, Error)]
33#[non_exhaustive]
34pub enum Error {
35 #[error(transparent)]
36 Iota(IotaError),
37
38 #[error(transparent)]
39 Internal(#[from] anyhow::Error),
40
41 #[error("Deserialization error: {0}")]
42 Bcs(#[from] bcs::Error),
43 #[error("Unexpected error: {0}")]
44 Unexpected(String),
45
46 #[error(transparent)]
47 RPCServer(#[from] RpcError),
48 #[error(transparent)]
49 RPCRegisterMethod(#[from] RegisterMethodError),
50
51 #[error(transparent)]
52 InvalidHeaderValue(#[from] InvalidHeaderValue),
53
54 #[error(transparent)]
55 UserInput(#[from] UserInputError),
56
57 #[error(transparent)]
58 Encoding(#[from] eyre::Report),
59
60 #[error(transparent)]
61 TokioJoin(#[from] JoinError),
62
63 #[error(transparent)]
64 QuorumDriver(#[from] QuorumDriverError),
65
66 #[error(transparent)]
67 FastCrypto(#[from] FastCryptoError),
68
69 #[error(transparent)]
70 IotaObjectResponse(#[from] IotaObjectResponseError),
71
72 #[error(transparent)]
73 IotaRpcInput(#[from] IotaRpcInputError),
74
75 #[error(transparent)]
77 StateRead(#[from] StateReadError),
78
79 #[error("Unsupported Feature: {0}")]
80 UnsupportedFeature(String),
81
82 #[error(transparent)]
83 IotaNames(#[from] IotaNamesError),
84}
85
86impl From<IotaError> for Error {
87 fn from(e: IotaError) -> Self {
88 match e {
89 IotaError::UserInput { error } => Self::UserInput(error),
90 IotaError::IotaObjectResponse { error } => Self::IotaObjectResponse(error),
91 IotaError::UnsupportedFeature { error } => Self::UnsupportedFeature(error),
92 IotaError::IndexStoreNotAvailable => Self::UnsupportedFeature(
93 "Required indexes are not available on this node".to_string(),
94 ),
95 other => Self::Iota(other),
96 }
97 }
98}
99
100impl From<Error> for RpcError {
101 fn from(e: Error) -> RpcError {
103 match e {
104 Error::UserInput(_) | Error::UnsupportedFeature(_) => RpcError::Call(
105 ErrorObject::owned::<()>(ErrorCode::InvalidRequest.code(), e.to_string(), None),
106 ),
107 Error::IotaObjectResponse(err) => match err {
108 IotaObjectResponseError::NotExists { .. }
109 | IotaObjectResponseError::DynamicFieldNotFound { .. }
110 | IotaObjectResponseError::Deleted { .. }
111 | IotaObjectResponseError::Display { .. } => {
112 RpcError::Call(ErrorObject::owned::<()>(
113 ErrorCode::InvalidParams.code(),
114 err.to_string(),
115 None,
116 ))
117 }
118 _ => RpcError::Call(ErrorObject::owned::<()>(
119 CALL_EXECUTION_FAILED_CODE,
120 err.to_string(),
121 None,
122 )),
123 },
124 Error::IotaRpcInput(err) => RpcError::Call(ErrorObject::owned::<()>(
125 ErrorCode::InvalidParams.code(),
126 err.to_string(),
127 None,
128 )),
129 Error::Iota(iota_error) => match iota_error {
130 IotaError::TransactionNotFound { .. }
131 | IotaError::TransactionsNotFound { .. }
132 | IotaError::TransactionEventsNotFound { .. } => {
133 RpcError::Call(ErrorObject::owned::<()>(
134 ErrorCode::InvalidParams.code(),
135 iota_error.to_string(),
136 None,
137 ))
138 }
139 _ => RpcError::Call(ErrorObject::owned::<()>(
140 CALL_EXECUTION_FAILED_CODE,
141 iota_error.to_string(),
142 None,
143 )),
144 },
145 Error::StateRead(err) => match err {
146 StateReadError::Client(_) => RpcError::Call(ErrorObject::owned::<()>(
147 ErrorCode::InvalidParams.code(),
148 err.to_string(),
149 None,
150 )),
151 _ => {
152 let error_object = ErrorObject::owned::<()>(
153 jsonrpsee::types::error::INTERNAL_ERROR_CODE,
154 err.to_string(),
155 None,
156 );
157 RpcError::Call(error_object)
158 }
159 },
160 Error::QuorumDriver(err) => {
161 match err {
162 QuorumDriverError::InvalidUserSignature(err) => {
163 let inner_error_str = match err {
164 IotaError::UserInput { error } => error.to_string(),
167 _ => err.to_string(),
168 };
169
170 let error_message = format!("Invalid user signature: {inner_error_str}");
171
172 let error_object = ErrorObject::owned::<()>(
173 TRANSACTION_EXECUTION_CLIENT_ERROR_CODE,
174 error_message,
175 None,
176 );
177 RpcError::Call(error_object)
178 }
179 QuorumDriverError::TxAlreadyFinalizedWithDifferentUserSignatures => {
180 let error_object = ErrorObject::owned::<()>(
181 TRANSACTION_EXECUTION_CLIENT_ERROR_CODE,
182 "The transaction is already finalized but with different user signatures",
183 None,
184 );
185 RpcError::Call(error_object)
186 }
187 QuorumDriverError::TimeoutBeforeFinality
188 | QuorumDriverError::FailedWithTransientErrorAfterMaximumAttempts { .. } => {
189 let error_object =
190 ErrorObject::owned::<()>(TRANSIENT_ERROR_CODE, err.to_string(), None);
191 RpcError::Call(error_object)
192 }
193 QuorumDriverError::ObjectsDoubleUsed {
194 conflicting_txes,
195 retried_tx,
196 retried_tx_success,
197 } => {
198 let error_message = format!(
199 "Failed to sign transaction by a quorum of validators because of locked objects. Retried a conflicting transaction {:?}, success: {:?}",
200 retried_tx, retried_tx_success
201 );
202
203 let new_map = conflicting_txes
204 .into_iter()
205 .map(|(digest, (pairs, _))| {
206 (
207 digest,
208 pairs.into_iter().map(|(_, obj_ref)| obj_ref).collect(),
209 )
210 })
211 .collect::<BTreeMap<_, Vec<_>>>();
212
213 let error_object = ErrorObject::owned(
214 TRANSACTION_EXECUTION_CLIENT_ERROR_CODE,
215 error_message,
216 Some(new_map),
217 );
218 RpcError::Call(error_object)
219 }
220 QuorumDriverError::NonRecoverableTransactionError { errors } => {
221 let new_errors: Vec<String> = errors
222 .into_iter()
223 .sorted_by(|(_, a, _), (_, b, _)| b.cmp(a))
226 .filter_map(|(err, _, _)| {
227 match &err {
228 IotaError::UserInput { error } => Some(error.to_string()),
239 _ => {
240 if err.is_retryable().0 {
241 None
242 } else {
243 Some(err.to_string())
244 }
245 }
246 }
247 })
248 .collect();
249
250 assert!(
251 !new_errors.is_empty(),
252 "NonRecoverableTransactionError should have at least one non-retryable error"
253 );
254
255 let error_list = new_errors.join(", ");
256 let error_msg = format!(
257 "Transaction execution failed due to issues with transaction inputs, please review the errors and try again: {}.",
258 error_list
259 );
260
261 let error_object = ErrorObject::owned::<()>(
262 TRANSACTION_EXECUTION_CLIENT_ERROR_CODE,
263 error_msg,
264 None,
265 );
266 RpcError::Call(error_object)
267 }
268 QuorumDriverError::QuorumDriverInternal(_) => {
269 let error_object = ErrorObject::owned::<()>(
270 INTERNAL_ERROR_CODE,
271 "Internal error occurred while executing transaction.",
272 None,
273 );
274 RpcError::Call(error_object)
275 }
276 QuorumDriverError::SystemOverload { .. }
277 | QuorumDriverError::SystemOverloadRetryAfter { .. } => {
278 let error_object =
279 ErrorObject::owned::<()>(TRANSIENT_ERROR_CODE, err.to_string(), None);
280 RpcError::Call(error_object)
281 }
282 }
283 }
284 _ => RpcError::Call(ErrorObject::owned::<()>(
285 CALL_EXECUTION_FAILED_CODE,
286 e.to_string(),
287 None,
288 )),
289 }
290 }
291}
292
293impl From<Error> for ErrorObjectOwned {
294 fn from(value: Error) -> Self {
295 error_object_from_rpc(value.into())
296 }
297}
298
299#[derive(Debug, Error)]
300pub enum IotaRpcInputError {
301 #[error("Input contains duplicates")]
302 ContainsDuplicates,
303
304 #[error("Input exceeds limit of {0}")]
305 SizeLimitExceeded(String),
306
307 #[error("{0}")]
308 GenericNotFound(String),
309
310 #[error("{0}")]
311 GenericInvalid(String),
312
313 #[error(
314 "request_type` must set to `None` or `WaitForLocalExecution` if effects is required in the response"
315 )]
316 InvalidExecuteTransactionRequestType,
317
318 #[error("Unsupported protocol version requested. Min supported: {0}, max supported: {1}")]
319 ProtocolVersionUnsupported(u64, u64),
320
321 #[error("{0}")]
322 CannotParseIotaStructTag(String),
323
324 #[error(transparent)]
325 Base64(#[from] eyre::Report),
326
327 #[error("Deserialization error: {0}")]
328 Bcs(#[from] bcs::Error),
329
330 #[error(transparent)]
331 FastCrypto(#[from] FastCryptoError),
332
333 #[error(transparent)]
334 Anyhow(#[from] anyhow::Error),
335
336 #[error(transparent)]
337 UserInput(#[from] UserInputError),
338}
339
340impl From<IotaRpcInputError> for RpcError {
341 fn from(e: IotaRpcInputError) -> Self {
342 RpcError::Call(ErrorObject::owned::<()>(
343 ErrorCode::InvalidParams.code(),
344 e.to_string(),
345 None,
346 ))
347 }
348}
349
350impl From<IotaRpcInputError> for ErrorObjectOwned {
351 fn from(value: IotaRpcInputError) -> Self {
352 error_object_from_rpc(value.into())
353 }
354}
355
356#[cfg(test)]
357mod tests {
358 use expect_test::expect;
359 use iota_types::{
360 base_types::{AuthorityName, ObjectID, ObjectRef, SequenceNumber},
361 committee::StakeUnit,
362 crypto::{AuthorityPublicKey, AuthorityPublicKeyBytes},
363 digests::{ObjectDigest, TransactionDigest},
364 };
365
366 use super::*;
367
368 fn test_object_ref() -> ObjectRef {
369 (
370 ObjectID::ZERO,
371 SequenceNumber::from_u64(0),
372 ObjectDigest::new([0; 32]),
373 )
374 }
375
376 mod match_quorum_driver_error_tests {
377 use super::*;
378
379 #[test]
380 fn test_invalid_user_signature() {
381 let quorum_driver_error =
382 QuorumDriverError::InvalidUserSignature(IotaError::InvalidSignature {
383 error: "Test inner invalid signature".to_string(),
384 });
385
386 let rpc_error: RpcError = Error::QuorumDriver(quorum_driver_error).into();
387
388 let error_object = error_object_from_rpc(rpc_error);
389 let expected_code = expect!["-32002"];
390 expected_code.assert_eq(&error_object.code().to_string());
391 let expected_message = expect![
392 "Invalid user signature: Signature is not valid: Test inner invalid signature"
393 ];
394 expected_message.assert_eq(error_object.message());
395 }
396
397 #[test]
398 fn test_timeout_before_finality() {
399 let quorum_driver_error = QuorumDriverError::TimeoutBeforeFinality;
400
401 let rpc_error: RpcError = Error::QuorumDriver(quorum_driver_error).into();
402
403 let error_object = error_object_from_rpc(rpc_error);
404 let expected_code = expect!["-32050"];
405 expected_code.assert_eq(&error_object.code().to_string());
406 let expected_message = expect!["Transaction timed out before reaching finality"];
407 expected_message.assert_eq(error_object.message());
408 }
409
410 #[test]
411 fn test_failed_with_transient_error_after_maximum_attempts() {
412 let quorum_driver_error =
413 QuorumDriverError::FailedWithTransientErrorAfterMaximumAttempts {
414 total_attempts: 10,
415 };
416
417 let rpc_error: RpcError = Error::QuorumDriver(quorum_driver_error).into();
418
419 let error_object = error_object_from_rpc(rpc_error);
420 let expected_code = expect!["-32050"];
421 expected_code.assert_eq(&error_object.code().to_string());
422 let expected_message = expect![
423 "Transaction failed to reach finality with transient error after 10 attempts."
424 ];
425 expected_message.assert_eq(error_object.message());
426 }
427
428 #[test]
429 fn test_objects_double_used() {
430 use iota_types::crypto::VerifyingKey;
431 let mut conflicting_txes: BTreeMap<
432 TransactionDigest,
433 (Vec<(AuthorityName, ObjectRef)>, StakeUnit),
434 > = BTreeMap::new();
435 let tx_digest = TransactionDigest::default();
436 let object_ref = test_object_ref();
437 let stake_unit: StakeUnit = 10;
438 let authority_name = AuthorityPublicKeyBytes([0; AuthorityPublicKey::LENGTH]);
439 conflicting_txes.insert(tx_digest, (vec![(authority_name, object_ref)], stake_unit));
440
441 let quorum_driver_error = QuorumDriverError::ObjectsDoubleUsed {
442 conflicting_txes,
443 retried_tx: Some(TransactionDigest::default()),
444 retried_tx_success: Some(true),
445 };
446
447 let rpc_error: RpcError = Error::QuorumDriver(quorum_driver_error).into();
448
449 let error_object = error_object_from_rpc(rpc_error);
450 let expected_code = expect!["-32002"];
451 expected_code.assert_eq(&error_object.code().to_string());
452 let expected_message = expect![
453 "Failed to sign transaction by a quorum of validators because of locked objects. Retried a conflicting transaction Some(TransactionDigest(11111111111111111111111111111111)), success: Some(true)"
454 ];
455 expected_message.assert_eq(error_object.message());
456 let expected_data = expect![[
457 r#"{"11111111111111111111111111111111":[["0x0000000000000000000000000000000000000000000000000000000000000000",0,"11111111111111111111111111111111"]]}"#
458 ]];
459 let actual_data = error_object.data().unwrap().to_string();
460 expected_data.assert_eq(&actual_data);
461 }
462
463 #[test]
464 fn test_non_recoverable_transaction_error() {
465 let quorum_driver_error = QuorumDriverError::NonRecoverableTransactionError {
466 errors: vec![
467 (
468 IotaError::UserInput {
469 error: UserInputError::GasBalanceTooLow {
470 gas_balance: 10,
471 needed_gas_amount: 100,
472 },
473 },
474 0,
475 vec![],
476 ),
477 (
478 IotaError::UserInput {
479 error: UserInputError::ObjectVersionUnavailableForConsumption {
480 provided_obj_ref: test_object_ref(),
481 current_version: 10.into(),
482 },
483 },
484 0,
485 vec![],
486 ),
487 ],
488 };
489
490 let rpc_error: RpcError = Error::QuorumDriver(quorum_driver_error).into();
491
492 let error_object = error_object_from_rpc(rpc_error);
493 let expected_code = expect!["-32002"];
494 expected_code.assert_eq(&error_object.code().to_string());
495 let expected_message = expect![
496 "Transaction execution failed due to issues with transaction inputs, please review the errors and try again: Balance of gas object 10 is lower than the needed amount: 100, Object (0x0000000000000000000000000000000000000000000000000000000000000000, SequenceNumber(0), o#11111111111111111111111111111111) is not available for consumption, its current version: SequenceNumber(10)."
497 ];
498 expected_message.assert_eq(error_object.message());
499 }
500
501 #[test]
502 fn test_non_recoverable_transaction_error_with_transient_errors() {
503 let quorum_driver_error = QuorumDriverError::NonRecoverableTransactionError {
504 errors: vec![
505 (
506 IotaError::UserInput {
507 error: UserInputError::ObjectNotFound {
508 object_id: test_object_ref().0,
509 version: None,
510 },
511 },
512 0,
513 vec![],
514 ),
515 (
516 IotaError::Rpc("Hello".to_string(), "Testing".to_string()),
517 0,
518 vec![],
519 ),
520 ],
521 };
522
523 let rpc_error: RpcError = Error::QuorumDriver(quorum_driver_error).into();
524
525 let error_object = error_object_from_rpc(rpc_error);
526 let expected_code = expect!["-32002"];
527 expected_code.assert_eq(&error_object.code().to_string());
528 let expected_message = expect![
529 "Transaction execution failed due to issues with transaction inputs, please review the errors and try again: Could not find the referenced object 0x0000000000000000000000000000000000000000000000000000000000000000 at version None."
530 ];
531 expected_message.assert_eq(error_object.message());
532 }
533
534 #[test]
535 fn test_quorum_driver_internal_error() {
536 let quorum_driver_error =
537 QuorumDriverError::QuorumDriverInternal(IotaError::UnexpectedMessage);
538
539 let rpc_error: RpcError = Error::QuorumDriver(quorum_driver_error).into();
540
541 let error_object = error_object_from_rpc(rpc_error);
542 let expected_code = expect!["-32603"];
543 expected_code.assert_eq(&error_object.code().to_string());
544 let expected_message = expect!["Internal error occurred while executing transaction."];
545 expected_message.assert_eq(error_object.message());
546 }
547
548 #[test]
549 fn test_system_overload() {
550 let quorum_driver_error = QuorumDriverError::SystemOverload {
551 overloaded_stake: 10,
552 errors: vec![(IotaError::UnexpectedMessage, 0, vec![])],
553 };
554
555 let rpc_error: RpcError = Error::QuorumDriver(quorum_driver_error).into();
556
557 let error_object = error_object_from_rpc(rpc_error);
558 let expected_code = expect!["-32050"];
559 expected_code.assert_eq(&error_object.code().to_string());
560 let expected_message = expect![
561 "Transaction is not processed because 10 of validators by stake are overloaded with certificates pending execution."
562 ];
563 expected_message.assert_eq(error_object.message());
564 }
565 }
566}