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