1use crate::{
6 base_types::*,
7 committee::{Committee, EpochId, StakeUnit},
8 digests::CheckpointContentsDigest,
9 execution_status::{CommandArgumentError, CommandIndex, ExecutionErrorKind, ExecutionFailure},
10 messages_checkpoint::CheckpointSequenceNumber,
11 object::Owner,
12};
13
14use schemars::JsonSchema;
15use serde::{Deserialize, Serialize};
16use std::{collections::BTreeMap, fmt::Debug, slice::SliceIndex};
17use strum_macros::{AsRefStr, IntoStaticStr};
18use thiserror::Error;
19use tonic::Status;
20use typed_store_error::TypedStoreError;
21
22pub const TRANSACTION_NOT_FOUND_MSG_PREFIX: &str = "Could not find the referenced transaction";
23pub const TRANSACTIONS_NOT_FOUND_MSG_PREFIX: &str = "Could not find the referenced transactions";
24
25#[macro_export]
26macro_rules! fp_bail {
27 ($e:expr) => {
28 return Err($e)
29 };
30}
31
32#[macro_export(local_inner_macros)]
33macro_rules! fp_ensure {
34 ($cond:expr, $e:expr) => {
35 if !($cond) {
36 fp_bail!($e);
37 }
38 };
39}
40
41#[macro_export]
42macro_rules! exit_main {
43 ($result:expr) => {
44 match $result {
45 Ok(_) => (),
46 Err(err) => {
47 let err = format!("{:?}", err);
48 println!("{}", err.bold().red());
49 std::process::exit(1);
50 }
51 }
52 };
53}
54
55#[macro_export]
56macro_rules! make_invariant_violation {
57 ($($args:expr),* $(,)?) => {{
58 if cfg!(debug_assertions) {
59 panic!($($args),*)
60 }
61 $crate::error::ExecutionError::invariant_violation(format!($($args),*))
62 }}
63}
64
65#[macro_export]
66macro_rules! invariant_violation {
67 ($($args:expr),* $(,)?) => {
68 return Err(make_invariant_violation!($($args),*).into())
69 };
70}
71
72#[macro_export]
73macro_rules! assert_invariant {
74 ($cond:expr, $($args:expr),* $(,)?) => {{
75 if !$cond {
76 invariant_violation!($($args),*)
77 }
78 }};
79}
80
81#[macro_export]
84macro_rules! checked_as {
85 ($value:expr, $target_type:ty) => {{
86 let v = $value;
87 <$target_type>::try_from(v).map_err(|e| {
88 $crate::make_invariant_violation!(
89 "Value {} cannot be safely cast to {}: {:?}",
90 v,
91 stringify!($target_type),
92 e
93 )
94 })
95 }};
96}
97
98pub trait SafeIndex<T> {
103 fn safe_get<'a, I>(&'a self, index: I) -> Result<&'a I::Output, ExecutionError>
106 where
107 I: SliceIndex<[T]>,
108 T: 'a;
109
110 fn safe_get_mut<'a, I>(&'a mut self, index: I) -> Result<&'a mut I::Output, ExecutionError>
113 where
114 I: SliceIndex<[T]>,
115 T: 'a;
116}
117
118impl<T, C> SafeIndex<T> for C
119where
120 C: AsRef<[T]> + AsMut<[T]>,
121{
122 fn safe_get<'a, I>(&'a self, index: I) -> Result<&'a I::Output, ExecutionError>
123 where
124 I: SliceIndex<[T]>,
125 T: 'a,
126 {
127 let slice = self.as_ref();
128 let len = slice.len();
129 slice.get(index).ok_or_else(|| {
130 crate::make_invariant_violation!("Index out of bounds for collection of length {}", len)
131 })
132 }
133
134 fn safe_get_mut<'a, I>(&'a mut self, index: I) -> Result<&'a mut I::Output, ExecutionError>
135 where
136 I: SliceIndex<[T]>,
137 T: 'a,
138 {
139 let slice = self.as_mut();
140 let len = slice.len();
141 slice.get_mut(index).ok_or_else(|| {
142 crate::make_invariant_violation!("Index out of bounds for collection of length {}", len)
143 })
144 }
145}
146
147#[derive(
148 Eq, PartialEq, Clone, Debug, Serialize, Deserialize, Error, Hash, AsRefStr, IntoStaticStr,
149)]
150pub enum UserInputError {
151 #[error("Mutable object {object_id} cannot appear more than one in one transaction")]
152 MutableObjectUsedMoreThanOnce { object_id: ObjectID },
153 #[error("Wrong number of parameters for the transaction")]
154 ObjectInputArityViolation,
155 #[error(
156 "Could not find the referenced object {} at version {:?}",
157 object_id,
158 version
159 )]
160 ObjectNotFound {
161 object_id: ObjectID,
162 version: Option<SequenceNumber>,
163 },
164 #[error(
165 "Transaction needs to be rebuilt because object {} version {} ({}) is unavailable for consumption, current version: {current_version}",
166 .provided_obj_ref.0, .provided_obj_ref.1, .provided_obj_ref.2
167 )]
168 ObjectVersionUnavailableForConsumption {
169 provided_obj_ref: ObjectRef,
170 current_version: SequenceNumber,
171 },
172 #[error("Package verification failed: {err}")]
173 PackageVerificationTimeout { err: String },
174 #[error("Dependent package not found on-chain: {package_id}")]
175 DependentPackageNotFound { package_id: ObjectID },
176 #[error("Mutable parameter provided, immutable parameter expected")]
177 ImmutableParameterExpectedError { object_id: ObjectID },
178 #[error("Size limit exceeded: {limit} is {value}")]
179 SizeLimitExceeded { limit: String, value: String },
180 #[error(
181 "Object {child_id} is owned by object {parent_id}. \
182 Objects owned by other objects cannot be used as input arguments"
183 )]
184 InvalidChildObjectArgument {
185 child_id: ObjectID,
186 parent_id: ObjectID,
187 },
188 #[error("Invalid Object digest for object {object_id}. Expected digest : {expected_digest}")]
189 InvalidObjectDigest {
190 object_id: ObjectID,
191 expected_digest: ObjectDigest,
192 },
193 #[error("Sequence numbers above the maximal value are not usable for transfers")]
194 InvalidSequenceNumber,
195 #[error("A move object is expected, instead a move package is passed: {object_id}")]
196 MovePackageAsObject { object_id: ObjectID },
197 #[error("A move package is expected, instead a move object is passed: {object_id}")]
198 MoveObjectAsPackage { object_id: ObjectID },
199 #[error("Transaction was not signed by the correct sender: {}", error)]
200 IncorrectUserSignature { error: String },
201
202 #[error("Object used as shared is not shared")]
203 NotSharedObjectError,
204 #[error("The transaction inputs contain duplicated ObjectRef's")]
205 DuplicateObjectRefInput,
206
207 #[error("Transaction gas payment missing")]
209 MissingGasPayment,
210 #[error("Gas object is not an owned object with owner: {:?}", owner)]
211 GasObjectNotOwnedObject { owner: Owner },
212 #[error("Gas budget: {gas_budget} is higher than max: {max_budget}")]
213 GasBudgetTooHigh { gas_budget: u64, max_budget: u64 },
214 #[error("Gas budget: {gas_budget} is lower than min: {min_budget}")]
215 GasBudgetTooLow { gas_budget: u64, min_budget: u64 },
216 #[error(
217 "Balance of gas object {gas_balance} is lower than the needed amount: {needed_gas_amount}"
218 )]
219 GasBalanceTooLow {
220 gas_balance: u128,
221 needed_gas_amount: u128,
222 },
223 #[error("Transaction kind does not support Sponsored Transaction")]
224 UnsupportedSponsoredTransactionKind,
225 #[error("Gas price {gas_price} under reference gas price (RGP) {reference_gas_price}")]
226 GasPriceUnderRGP {
227 gas_price: u64,
228 reference_gas_price: u64,
229 },
230 #[error("Gas price cannot exceed {max_gas_price} mist")]
231 GasPriceTooHigh { max_gas_price: u64 },
232 #[error("Object {object_id} is not a gas object")]
233 InvalidGasObject { object_id: ObjectID },
234 #[error("Gas object does not have enough balance to cover minimal gas spend")]
235 InsufficientBalanceToCoverMinimalGas,
236
237 #[error(
238 "Could not find the referenced object {object_id} as the asked version {asked_version:?} is higher than the latest {latest_version:?}"
239 )]
240 ObjectSequenceNumberTooHigh {
241 object_id: ObjectID,
242 asked_version: SequenceNumber,
243 latest_version: SequenceNumber,
244 },
245 #[error("Object deleted at reference ({}, {:?}, {})", object_ref.0, object_ref.1, object_ref.2)]
246 ObjectDeleted { object_ref: ObjectRef },
247 #[error("Invalid Batch Transaction: {error}")]
248 InvalidBatchTransaction { error: String },
249 #[error("This Move function is currently disabled and not available for call")]
250 BlockedMoveFunction,
251 #[error("Empty input coins for Pay related transaction")]
252 EmptyInputCoins,
253
254 #[error(
255 "SUI payment transactions use first input coin for gas payment, but found a different gas object"
256 )]
257 UnexpectedGasPaymentObject,
258
259 #[error("Wrong initial version given for shared object")]
260 SharedObjectStartingVersionMismatch,
261
262 #[error(
263 "Attempt to transfer object {object_id} that does not have public transfer. Object transfer must be done instead using a distinct Move function call"
264 )]
265 TransferObjectWithoutPublicTransferError { object_id: ObjectID },
266
267 #[error(
268 "TransferObjects, MergeCoin, and Publish cannot have empty arguments. \
269 If MakeMoveVec has empty arguments, it must have a type specified"
270 )]
271 EmptyCommandInput,
272
273 #[error("Transaction is denied: {error}")]
274 TransactionDenied { error: String },
275
276 #[error("Feature is not supported: {0}")]
277 Unsupported(String),
278
279 #[error("Query transactions with move function input error: {0}")]
280 MoveFunctionInputError(String),
281
282 #[error("Verified checkpoint not found for sequence number: {0}")]
283 VerifiedCheckpointNotFound(CheckpointSequenceNumber),
284
285 #[error("Verified checkpoint not found for digest: {0}")]
286 VerifiedCheckpointDigestNotFound(String),
287
288 #[error("Latest checkpoint sequence number not found")]
289 LatestCheckpointSequenceNumberNotFound,
290
291 #[error("Checkpoint contents not found for digest: {0}")]
292 CheckpointContentsNotFound(CheckpointContentsDigest),
293
294 #[error("Genesis transaction not found")]
295 GenesisTransactionNotFound,
296
297 #[error("Transaction {0} not found")]
298 TransactionCursorNotFound(u64),
299
300 #[error(
301 "Object {} is a system object and cannot be accessed by user transactions",
302 object_id
303 )]
304 InaccessibleSystemObject { object_id: ObjectID },
305 #[error(
306 "{max_publish_commands} max publish/upgrade commands allowed, {publish_count} provided"
307 )]
308 MaxPublishCountExceeded {
309 max_publish_commands: u64,
310 publish_count: u64,
311 },
312
313 #[error("Immutable parameter provided, mutable parameter expected")]
314 MutableParameterExpected { object_id: ObjectID },
315
316 #[error("Address {address} is denied for coin {coin_type}")]
317 AddressDeniedForCoin {
318 address: SuiAddress,
319 coin_type: String,
320 },
321
322 #[error("Commands following a command with Random can only be TransferObjects or MergeCoins")]
323 PostRandomCommandRestrictions,
324
325 #[error(
326 "Invalid argument at command {command_idx}, argument {argument_idx}: index {index} is out of bounds"
327 )]
328 InvalidArgumentIndex {
329 command_idx: usize,
330 argument_idx: usize,
331 index: u16,
332 },
333
334 #[error("Number of transactions ({size}) exceeds the maximum allowed ({limit}) in a batch")]
336 TooManyTransactionsInBatch { size: usize, limit: u64 },
337 #[error(
338 "Total transactions size ({size}) bytes exceeds the maximum allowed ({limit}) bytes in a Soft Bundle"
339 )]
340 TotalTransactionSizeTooLargeInBatch { size: usize, limit: u64 },
341 #[error("Transaction {digest} in Soft Bundle contains no shared objects")]
342 NoSharedObjectError { digest: TransactionDigest },
343 #[error("Transaction {digest} in Soft Bundle has already been executed")]
344 AlreadyExecutedInSoftBundleError { digest: TransactionDigest },
345 #[error("At least one certificate in Soft Bundle has already been processed")]
346 CertificateAlreadyProcessed,
347 #[error("Transaction {digest} was already executed")]
348 TransactionAlreadyExecuted { digest: TransactionDigest },
349 #[error(
350 "Gas price for transaction {digest} in Soft Bundle mismatch: want {expected}, have {actual}"
351 )]
352 GasPriceMismatchError {
353 digest: TransactionDigest,
354 expected: u64,
355 actual: u64,
356 },
357
358 #[error("Coin type is globally paused for use: {coin_type}")]
359 CoinTypeGlobalPause { coin_type: String },
360
361 #[error("Invalid identifier found in the transaction: {error}")]
362 InvalidIdentifier { error: String },
363
364 #[error("Object used as owned is not owned")]
365 NotOwnedObjectError,
366
367 #[error("Invalid withdraw reservation: {error}")]
368 InvalidWithdrawReservation { error: String },
369
370 #[error("Transaction with empty gas payment must specify an expiration.")]
371 MissingTransactionExpiration,
372
373 #[error("Invalid transaction expiration: {error}")]
374 InvalidExpiration { error: String },
375
376 #[error("Transaction chain ID {provided} does not match network chain ID {expected}.")]
377 InvalidChainId { provided: String, expected: String },
378
379 #[error("Transaction {digest} appears more than once in the request")]
380 RepeatedTransactions { digest: TransactionDigest },
381
382 #[error("Validator {proposer} is not an allowed proposer of this transaction")]
383 ProposerNotAllowed { proposer: u32 },
384}
385
386#[derive(
387 Eq,
388 PartialEq,
389 Clone,
390 Debug,
391 Serialize,
392 Deserialize,
393 Hash,
394 AsRefStr,
395 IntoStaticStr,
396 JsonSchema,
397 Error,
398)]
399#[serde(tag = "code", rename = "ObjectResponseError", rename_all = "camelCase")]
400pub enum SuiObjectResponseError {
401 #[error("Object {object_id} does not exist")]
402 NotExists { object_id: ObjectID },
403 #[error("Cannot find dynamic field for parent object {parent_object_id}")]
404 DynamicFieldNotFound { parent_object_id: ObjectID },
405 #[error(
406 "Object has been deleted object_id: {object_id} at version: {version:?} in digest {digest}"
407 )]
408 Deleted {
409 object_id: ObjectID,
410 version: SequenceNumber,
412 digest: ObjectDigest,
414 },
415 #[error("Unknown Error")]
416 Unknown,
417 #[error("Display Error: {error}")]
418 DisplayError { error: String },
419 }
421
422#[derive(Eq, PartialEq, Clone, Serialize, Deserialize, Error, Hash)]
424#[error(transparent)]
425pub struct SuiError(#[from] pub Box<SuiErrorKind>);
426
427#[derive(
429 Eq, PartialEq, Clone, Debug, Serialize, Deserialize, Error, Hash, AsRefStr, IntoStaticStr,
430)]
431pub enum SuiErrorKind {
432 #[error("Error checking transaction input objects: {error}")]
433 UserInputError { error: UserInputError },
434
435 #[error("Error checking transaction object: {error}")]
436 SuiObjectResponseError { error: SuiObjectResponseError },
437
438 #[error("Expecting a single owner, shared ownership found")]
439 UnexpectedOwnerType,
440
441 #[error("There are already {queue_len} transactions pending, above threshold of {threshold}")]
442 TooManyTransactionsPendingExecution { queue_len: usize, threshold: usize },
443
444 #[error("There are too many transactions pending in consensus")]
445 TooManyTransactionsPendingConsensus,
446
447 #[error(
448 "Input {object_id} already has {queue_len} transactions pending, above threshold of {threshold}"
449 )]
450 TooManyTransactionsPendingOnObject {
451 object_id: ObjectID,
452 queue_len: usize,
453 threshold: usize,
454 },
455
456 #[error(
457 "Input {object_id} has a transaction {txn_age_sec} seconds old pending, above threshold of {threshold} seconds"
458 )]
459 TooOldTransactionPendingOnObject {
460 object_id: ObjectID,
461 txn_age_sec: u64,
462 threshold: u64,
463 },
464
465 #[error("Soft bundle must only contain transactions of UserTransaction kind")]
466 InvalidTxKindInSoftBundle,
467
468 #[error("Signature is not valid: {}", error)]
470 InvalidSignature { error: String },
471 #[error("Required Signature from {expected} is absent {:?}", actual)]
472 SignerSignatureAbsent {
473 expected: String,
474 actual: Vec<String>,
475 },
476 #[error("Expect {expected} signer signatures but got {actual}")]
477 SignerSignatureNumberMismatch { expected: usize, actual: usize },
478 #[error("Value was not signed by the correct sender: {}", error)]
479 IncorrectSigner { error: String },
480 #[error(
481 "Value was not signed by a known authority. signer: {:?}, index: {:?}, committee: {committee}",
482 signer,
483 index
484 )]
485 UnknownSigner {
486 signer: Option<String>,
487 index: Option<u32>,
488 committee: Box<Committee>,
489 },
490 #[error(
491 "Validator {:?} responded multiple signatures for the same message, conflicting: {:?}",
492 signer,
493 conflicting_sig
494 )]
495 StakeAggregatorRepeatedSigner {
496 signer: AuthorityName,
497 conflicting_sig: bool,
498 },
499 #[error(
501 "Signature is not valid, but a retry may result in a valid one: {}",
502 error
503 )]
504 PotentiallyTemporarilyInvalidSignature { error: String },
505
506 #[error(
508 "Signature or certificate from wrong epoch, expected {expected_epoch}, got {actual_epoch}"
509 )]
510 WrongEpoch {
511 expected_epoch: EpochId,
512 actual_epoch: EpochId,
513 },
514 #[error("Signatures in a certificate must form a quorum")]
515 CertificateRequiresQuorum,
516 #[allow(non_camel_case_types)]
517 #[error("DEPRECATED")]
518 DEPRECATED_ErrorWhileProcessingCertificate,
519 #[error(
520 "Failed to get a quorum of signed effects when processing transaction: {effects_map:?}"
521 )]
522 QuorumFailedToGetEffectsQuorumWhenProcessingTransaction {
523 effects_map: BTreeMap<TransactionEffectsDigest, (Vec<AuthorityName>, StakeUnit)>,
524 },
525 #[error(
526 "Failed to verify Tx certificate with executed effects, error: {error:?}, validator: {validator_name:?}"
527 )]
528 FailedToVerifyTxCertWithExecutedEffects {
529 validator_name: AuthorityName,
530 error: String,
531 },
532 #[error("Transaction is already finalized but with different user signatures")]
533 TxAlreadyFinalizedWithDifferentUserSigs,
534
535 #[error("Invalid authenticator")]
537 InvalidAuthenticator,
538 #[error("Invalid address")]
539 InvalidAddress,
540 #[error("Invalid transaction digest.")]
541 InvalidTransactionDigest,
542
543 #[error("Invalid digest length. Expected {expected}, got {actual}")]
544 InvalidDigestLength { expected: usize, actual: usize },
545 #[error("Invalid DKG message size")]
546 InvalidDkgMessageSize,
547
548 #[error("Unexpected message: {0}")]
549 UnexpectedMessage(String),
550
551 #[error("Failed to verify the Move module, reason: {error}.")]
553 ModuleVerificationFailure { error: String },
554 #[error("Failed to deserialize the Move module, reason: {error}.")]
555 ModuleDeserializationFailure { error: String },
556 #[error("Failed to publish the Move module(s), reason: {error}")]
557 ModulePublishFailure { error: String },
558 #[error("Failed to build Move modules: {error}.")]
559 ModuleBuildFailure { error: String },
560
561 #[error("Function resolution failure: {error}.")]
563 FunctionNotFound { error: String },
564 #[error("Module not found in package: {module_name}.")]
565 ModuleNotFound { module_name: String },
566 #[error("Type error while binding function arguments: {error}.")]
567 TypeError { error: String },
568 #[error("Circular object ownership detected")]
569 CircularObjectOwnership,
570
571 #[error("Attempt to re-initialize a transaction lock for objects {:?}.", refs)]
573 ObjectLockAlreadyInitialized { refs: Vec<ObjectRef> },
574 #[error(
575 "Object {obj_ref:?} already locked by a different transaction: {pending_transaction:?}"
576 )]
577 ObjectLockConflict {
578 obj_ref: ObjectRef,
579 pending_transaction: TransactionDigest,
580 },
581 #[error(
582 "Objects {obj_refs:?} are already locked by a transaction from a future epoch {locked_epoch:?}), attempt to override with a transaction from epoch {new_epoch:?}"
583 )]
584 ObjectLockedAtFutureEpoch {
585 obj_refs: Vec<ObjectRef>,
586 locked_epoch: EpochId,
587 new_epoch: EpochId,
588 locked_by_tx: TransactionDigest,
589 },
590 #[error("{TRANSACTION_NOT_FOUND_MSG_PREFIX} [{:?}].", digest)]
591 TransactionNotFound { digest: TransactionDigest },
592 #[error("{TRANSACTIONS_NOT_FOUND_MSG_PREFIX} [{:?}].", digests)]
593 TransactionsNotFound { digests: Vec<TransactionDigest> },
594 #[error("Could not find the referenced transaction events [{digest:?}].")]
595 TransactionEventsNotFound { digest: TransactionDigest },
596 #[error("Could not find the referenced transaction effects [{digest:?}].")]
597 TransactionEffectsNotFound { digest: TransactionDigest },
598 #[error(
599 "Attempt to move to `Executed` state an transaction that has already been executed: {:?}.",
600 digest
601 )]
602 TransactionAlreadyExecuted { digest: TransactionDigest },
603 #[error("Transaction reject reason not found for transaction {digest:?}")]
604 TransactionRejectReasonNotFound { digest: TransactionDigest },
605 #[error("Object ID did not have the expected type")]
606 BadObjectType { error: String },
607 #[error("Fail to retrieve Object layout for {st}")]
608 FailObjectLayout { st: String },
609
610 #[error("Execution invariant violated")]
611 ExecutionInvariantViolation,
612 #[error("Validator {authority:?} is faulty in a Byzantine manner: {reason:?}")]
613 ByzantineAuthoritySuspicion {
614 authority: AuthorityName,
615 reason: String,
616 },
617 #[allow(non_camel_case_types)]
618 #[serde(rename = "StorageError")]
619 #[error("DEPRECATED")]
620 DEPRECATED_StorageError,
621 #[allow(non_camel_case_types)]
622 #[serde(rename = "GenericStorageError")]
623 #[error("DEPRECATED")]
624 DEPRECATED_GenericStorageError,
625 #[error(
626 "Attempted to access {object} through parent {given_parent}, \
627 but it's actual parent is {actual_owner}"
628 )]
629 InvalidChildObjectAccess {
630 object: ObjectID,
631 given_parent: ObjectID,
632 actual_owner: Owner,
633 },
634
635 #[allow(non_camel_case_types)]
636 #[serde(rename = "StorageMissingFieldError")]
637 #[error("DEPRECATED")]
638 DEPRECATED_StorageMissingFieldError,
639 #[allow(non_camel_case_types)]
640 #[serde(rename = "StorageCorruptedFieldError")]
641 #[error("DEPRECATED")]
642 DEPRECATED_StorageCorruptedFieldError,
643
644 #[error("Authority Error: {error}")]
645 GenericAuthorityError { error: String },
646
647 #[error("Generic Bridge Error: {error}")]
648 GenericBridgeError { error: String },
649
650 #[error("Failed to dispatch subscription: {error}")]
651 FailedToDispatchSubscription { error: String },
652
653 #[error("Failed to serialize Owner: {error}")]
654 OwnerFailedToSerialize { error: String },
655
656 #[error("Failed to deserialize fields into JSON: {error}")]
657 ExtraFieldFailedToDeserialize { error: String },
658
659 #[error("Failed to execute transaction locally by Orchestrator: {error}")]
660 TransactionOrchestratorLocalExecutionError { error: String },
661
662 #[error("Failure serializing transaction in the requested format: {error}")]
664 TransactionSerializationError { error: String },
665 #[error("Failure deserializing transaction from the provided format: {error}")]
666 TransactionDeserializationError { error: String },
667 #[error("Failure serializing transaction effects from the provided format: {error}")]
668 TransactionEffectsSerializationError { error: String },
669 #[error("Failure deserializing transaction effects from the provided format: {error}")]
670 TransactionEffectsDeserializationError { error: String },
671 #[error("Failure serializing transaction events from the provided format: {error}")]
672 TransactionEventsSerializationError { error: String },
673 #[error("Failure deserializing transaction events from the provided format: {error}")]
674 TransactionEventsDeserializationError { error: String },
675 #[error("Failure serializing object in the requested format: {error}")]
676 ObjectSerializationError { error: String },
677 #[error("Failure deserializing object in the requested format: {error}")]
678 ObjectDeserializationError { error: String },
679 #[error("Event store component is not active on this node")]
680 NoEventStore,
681
682 #[error("Too many authority errors were detected for {}: {:?}", action, errors)]
684 TooManyIncorrectAuthorities {
685 errors: Vec<(AuthorityName, SuiError)>,
686 action: String,
687 },
688 #[error("Invalid transaction range query to the fullnode: {error}")]
689 FullNodeInvalidTxRangeQuery { error: String },
690
691 #[error("Failed to submit transaction to consensus: {0}")]
693 FailedToSubmitToConsensus(String),
694 #[error("Failed to connect with consensus node: {0}")]
695 ConsensusConnectionBroken(String),
696 #[error("Failed to execute handle_consensus_transaction on Sui: {0}")]
697 HandleConsensusTransactionFailure(String),
698
699 #[error("Signature key generation error: {0}")]
701 SignatureKeyGenError(String),
702 #[error("Key Conversion Error: {0}")]
703 KeyConversionError(String),
704 #[error("Invalid Private Key provided")]
705 InvalidPrivateKey,
706
707 #[error("Fullnode does not support handle_certificate")]
709 FullNodeCantHandleCertificate,
710
711 #[error("Validator temporarily stopped processing transactions due to epoch change")]
713 ValidatorHaltedAtEpochEnd,
714 #[error("Operations for epoch {0} have ended")]
715 EpochEnded(EpochId),
716 #[error("Error when advancing epoch: {error}")]
717 AdvanceEpochError { error: String },
718
719 #[error("Transaction Expired")]
720 TransactionExpired,
721
722 #[error("{1} - {0}")]
725 RpcError(String, String),
726
727 #[error("Method not allowed")]
728 InvalidRpcMethodError,
729
730 #[error("Use of disabled feature: {error}")]
731 UnsupportedFeatureError { error: String },
732
733 #[error("Unable to communicate with the Quorum Driver channel: {error}")]
734 QuorumDriverCommunicationError { error: String },
735
736 #[error("Operation timed out")]
737 TimeoutError,
738
739 #[error("Error executing {0}")]
740 ExecutionError(String),
741
742 #[error("Invalid committee composition")]
743 InvalidCommittee(String),
744
745 #[error("Missing committee information for epoch {0}")]
746 MissingCommitteeAtEpoch(EpochId),
747
748 #[error("Failed to read dynamic field from table in the object store: {0}")]
749 DynamicFieldReadError(String),
750
751 #[error("Failed to read or deserialize system state related data structures on-chain: {0}")]
752 SuiSystemStateReadError(String),
753
754 #[error("Failed to read or deserialize bridge related data structures on-chain: {0}")]
755 SuiBridgeReadError(String),
756
757 #[error("Unexpected version error: {0}")]
758 UnexpectedVersion(String),
759
760 #[error("Message version is not supported at the current protocol version: {error}")]
761 WrongMessageVersion { error: String },
762
763 #[error("unknown error: {0}")]
764 Unknown(String),
765
766 #[error("Failed to perform file operation: {0}")]
767 FileIOError(String),
768
769 #[error("Failed to get JWK")]
770 JWKRetrievalError,
771
772 #[error("Storage error: {0}")]
773 Storage(String),
774
775 #[error(
776 "Validator cannot handle the request at the moment. Please retry after at least {retry_after_secs} seconds."
777 )]
778 ValidatorOverloadedRetryAfter { retry_after_secs: u64 },
779
780 #[error("Too many requests")]
781 TooManyRequests,
782
783 #[error("The request did not contain a certificate")]
784 NoCertificateProvidedError,
785
786 #[error("Nitro attestation verify failed: {0}")]
787 NitroAttestationFailedToVerify(String),
788
789 #[error("Failed to serialize {type_info}, error: {error}")]
790 GrpcMessageSerializeError { type_info: String, error: String },
791
792 #[error("Failed to deserialize {type_info}, error: {error}")]
793 GrpcMessageDeserializeError { type_info: String, error: String },
794
795 #[error(
796 "Validator consensus rounds are lagging behind. last committed leader round: {last_committed_round}, requested round: {round}"
797 )]
798 ValidatorConsensusLagging {
799 round: u32,
800 last_committed_round: u32,
801 },
802
803 #[error("Invalid admin request: {0}")]
804 InvalidAdminRequest(String),
805
806 #[error("Invalid request: {0}")]
807 InvalidRequest(String),
808
809 #[error(
810 "The current set of aliases for a required signer changed after the transaction was submitted"
811 )]
812 AliasesChanged,
813
814 #[error("Object {object_id} not found among input objects.")]
816 ImmutableObjectClaimNotFoundInInput { object_id: ObjectID },
817
818 #[error("Immutable object {object_id} was not included in immutable claims.")]
820 ImmutableObjectNotClaimed { object_id: ObjectID },
821
822 #[error(
824 "Claimed object {claimed_object_id} is not immutable. Found object ref: {found_object_ref:?}"
825 )]
826 InvalidImmutableObjectClaim {
827 claimed_object_id: ObjectID,
828 found_object_ref: ObjectRef,
829 },
830
831 #[error(
832 "Transaction was outbid by higher-gas-price transactions in the admission queue (current minimum gas price required: {min_gas_price})"
833 )]
834 TransactionRejectedDueToOutbiddingDuringCongestion { min_gas_price: u64 },
835
836 #[error("Transaction {digest} is being processed post-consensus: {status}")]
837 TransactionProcessing {
838 digest: TransactionDigest,
839 status: String,
840 },
841
842 #[error("Transaction {digest} has been recently submitted to this validator.")]
843 TransactionSubmitted { digest: TransactionDigest },
844
845 #[error(
848 "This validator is outside the transaction's staggered-submission proposer set; submit to one of its proposers."
849 )]
850 StaggeredSubmissionNotProposer,
851}
852
853#[repr(u64)]
854#[allow(non_camel_case_types)]
855#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
856pub enum VMMVerifierErrorSubStatusCode {
859 MULTIPLE_RETURN_VALUES_NOT_ALLOWED = 0,
860 INVALID_OBJECT_CREATION = 1,
861}
862
863#[repr(u64)]
864#[allow(non_camel_case_types)]
865#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
866pub enum VMMemoryLimitExceededSubStatusCode {
868 EVENT_COUNT_LIMIT_EXCEEDED = 0,
869 EVENT_SIZE_LIMIT_EXCEEDED = 1,
870 NEW_ID_COUNT_LIMIT_EXCEEDED = 2,
871 DELETED_ID_COUNT_LIMIT_EXCEEDED = 3,
872 TRANSFER_ID_COUNT_LIMIT_EXCEEDED = 4,
873 OBJECT_RUNTIME_CACHE_LIMIT_EXCEEDED = 5,
874 OBJECT_RUNTIME_STORE_LIMIT_EXCEEDED = 6,
875 TOTAL_EVENT_SIZE_LIMIT_EXCEEDED = 7,
876 SCRATCH_SIZE_LIMIT_EXCEEDED = 8,
877 ACCUMULATOR_EVENT_COUNT_LIMIT_EXCEEDED = 9,
878}
879
880pub type SuiResult<T = ()> = Result<T, SuiError>;
881pub type UserInputResult<T = ()> = Result<T, UserInputError>;
882
883impl From<SuiErrorKind> for SuiError {
884 fn from(error: SuiErrorKind) -> Self {
885 SuiError(Box::new(error))
886 }
887}
888
889impl std::ops::Deref for SuiError {
890 type Target = SuiErrorKind;
891
892 fn deref(&self) -> &Self::Target {
893 &self.0
894 }
895}
896
897impl From<sui_protocol_config::Error> for SuiError {
898 fn from(error: sui_protocol_config::Error) -> Self {
899 SuiErrorKind::WrongMessageVersion { error: error.0 }.into()
900 }
901}
902
903impl From<ExecutionError> for SuiError {
904 fn from(error: ExecutionError) -> Self {
905 SuiErrorKind::ExecutionError(error.to_string()).into()
906 }
907}
908
909impl From<Status> for SuiError {
910 fn from(status: Status) -> Self {
911 if status.message() == "Too many requests" {
912 return SuiErrorKind::TooManyRequests.into();
913 }
914
915 let result = bcs::from_bytes::<SuiError>(status.details());
916 if let Ok(sui_error) = result {
917 sui_error
918 } else {
919 SuiErrorKind::RpcError(
920 status.message().to_owned(),
921 status.code().description().to_owned(),
922 )
923 .into()
924 }
925 }
926}
927
928impl From<TypedStoreError> for SuiError {
929 fn from(e: TypedStoreError) -> Self {
930 SuiErrorKind::Storage(e.to_string()).into()
931 }
932}
933
934impl From<crate::storage::error::Error> for SuiError {
935 fn from(e: crate::storage::error::Error) -> Self {
936 SuiErrorKind::Storage(e.to_string()).into()
937 }
938}
939
940impl From<SuiErrorKind> for Status {
941 fn from(error: SuiErrorKind) -> Self {
942 let bytes = bcs::to_bytes(&error).unwrap();
943 Status::with_details(tonic::Code::Internal, error.to_string(), bytes.into())
944 }
945}
946
947impl From<SuiError> for Status {
948 fn from(error: SuiError) -> Self {
949 Status::from(error.into_inner())
950 }
951}
952
953impl From<ExecutionErrorKind> for SuiError {
954 fn from(kind: ExecutionErrorKind) -> Self {
955 ExecutionError::from_kind(kind).into()
956 }
957}
958
959impl From<&str> for SuiError {
960 fn from(error: &str) -> Self {
961 SuiErrorKind::GenericAuthorityError {
962 error: error.to_string(),
963 }
964 .into()
965 }
966}
967
968impl From<String> for SuiError {
969 fn from(error: String) -> Self {
970 SuiErrorKind::GenericAuthorityError { error }.into()
971 }
972}
973
974impl TryFrom<SuiErrorKind> for UserInputError {
975 type Error = anyhow::Error;
976
977 fn try_from(err: SuiErrorKind) -> Result<Self, Self::Error> {
978 match err {
979 SuiErrorKind::UserInputError { error } => Ok(error),
980 other => anyhow::bail!("error {:?} is not UserInputError", other),
981 }
982 }
983}
984
985impl TryFrom<SuiError> for UserInputError {
986 type Error = anyhow::Error;
987
988 fn try_from(err: SuiError) -> Result<Self, Self::Error> {
989 err.into_inner().try_into()
990 }
991}
992
993impl From<UserInputError> for SuiError {
994 fn from(error: UserInputError) -> Self {
995 SuiErrorKind::UserInputError { error }.into()
996 }
997}
998
999impl From<SuiObjectResponseError> for SuiError {
1000 fn from(error: SuiObjectResponseError) -> Self {
1001 SuiErrorKind::SuiObjectResponseError { error }.into()
1002 }
1003}
1004
1005impl PartialEq<SuiErrorKind> for SuiError {
1006 fn eq(&self, other: &SuiErrorKind) -> bool {
1007 &*self.0 == other
1008 }
1009}
1010
1011impl PartialEq<SuiError> for SuiErrorKind {
1012 fn eq(&self, other: &SuiError) -> bool {
1013 self == &*other.0
1014 }
1015}
1016
1017impl SuiError {
1018 pub fn as_inner(&self) -> &SuiErrorKind {
1019 &self.0
1020 }
1021
1022 pub fn into_inner(self) -> SuiErrorKind {
1023 *self.0
1024 }
1025}
1026
1027impl SuiErrorKind {
1028 pub fn to_variant_name(&self) -> &'static str {
1030 match &self {
1031 SuiErrorKind::UserInputError { error } => error.into(),
1032 _ => self.into(),
1033 }
1034 }
1035
1036 pub fn individual_error_indicates_epoch_change(&self) -> bool {
1037 matches!(
1038 self,
1039 SuiErrorKind::ValidatorHaltedAtEpochEnd | SuiErrorKind::MissingCommitteeAtEpoch(_)
1040 )
1041 }
1042
1043 pub fn is_retryable(&self) -> (bool, bool) {
1048 let retryable = match self {
1049 SuiErrorKind::RpcError { .. } => true,
1051
1052 SuiErrorKind::ValidatorHaltedAtEpochEnd => true,
1054 SuiErrorKind::MissingCommitteeAtEpoch(..) => true,
1055 SuiErrorKind::WrongEpoch { .. } => true,
1056 SuiErrorKind::EpochEnded(..) => true,
1057
1058 SuiErrorKind::UserInputError { error } => {
1059 match error {
1060 UserInputError::ObjectNotFound { .. } => true,
1062 UserInputError::DependentPackageNotFound { .. } => true,
1063 _ => false,
1064 }
1065 }
1066
1067 SuiErrorKind::PotentiallyTemporarilyInvalidSignature { .. } => true,
1068
1069 SuiErrorKind::TooManyTransactionsPendingExecution { .. } => true,
1071 SuiErrorKind::TooManyTransactionsPendingOnObject { .. } => true,
1072 SuiErrorKind::TooOldTransactionPendingOnObject { .. } => true,
1073 SuiErrorKind::TooManyTransactionsPendingConsensus => true,
1074 SuiErrorKind::TransactionRejectedDueToOutbiddingDuringCongestion { .. } => true,
1075 SuiErrorKind::ValidatorOverloadedRetryAfter { .. } => true,
1076
1077 SuiErrorKind::StaggeredSubmissionNotProposer => true,
1079
1080 SuiErrorKind::TransactionProcessing { .. } => true,
1084 SuiErrorKind::TransactionSubmitted { .. } => true,
1085
1086 SuiErrorKind::ExecutionError(..) => false,
1088 SuiErrorKind::ByzantineAuthoritySuspicion { .. } => false,
1089 SuiErrorKind::QuorumFailedToGetEffectsQuorumWhenProcessingTransaction { .. } => false,
1090 SuiErrorKind::TxAlreadyFinalizedWithDifferentUserSigs => false,
1091 SuiErrorKind::FailedToVerifyTxCertWithExecutedEffects { .. } => false,
1092 SuiErrorKind::ObjectLockConflict { .. } => false,
1093
1094 SuiErrorKind::TooManyRequests => false,
1098
1099 _ => return (false, false),
1101 };
1102
1103 (retryable, true)
1104 }
1105
1106 pub fn is_object_or_package_not_found(&self) -> bool {
1107 match self {
1108 SuiErrorKind::UserInputError { error } => {
1109 matches!(
1110 error,
1111 UserInputError::ObjectNotFound { .. }
1112 | UserInputError::DependentPackageNotFound { .. }
1113 )
1114 }
1115 _ => false,
1116 }
1117 }
1118
1119 pub fn is_overload(&self) -> bool {
1120 matches!(
1121 self,
1122 SuiErrorKind::TooManyTransactionsPendingExecution { .. }
1123 | SuiErrorKind::TooManyTransactionsPendingOnObject { .. }
1124 | SuiErrorKind::TooOldTransactionPendingOnObject { .. }
1125 | SuiErrorKind::TooManyTransactionsPendingConsensus
1126 | SuiErrorKind::TransactionRejectedDueToOutbiddingDuringCongestion { .. }
1127 )
1128 }
1129
1130 pub fn is_retryable_overload(&self) -> bool {
1131 matches!(self, SuiErrorKind::ValidatorOverloadedRetryAfter { .. })
1132 }
1133
1134 pub fn retry_after_secs(&self) -> u64 {
1135 match self {
1136 SuiErrorKind::ValidatorOverloadedRetryAfter { retry_after_secs } => *retry_after_secs,
1137 _ => 0,
1138 }
1139 }
1140
1141 pub fn categorize(&self) -> ErrorCategory {
1143 match self {
1144 SuiErrorKind::UserInputError { error } => {
1145 match error {
1146 UserInputError::ObjectNotFound { .. } => ErrorCategory::Aborted,
1149 UserInputError::DependentPackageNotFound { .. } => ErrorCategory::Aborted,
1150 _ => ErrorCategory::InvalidTransaction,
1152 }
1153 }
1154
1155 SuiErrorKind::InvalidSignature { .. }
1156 | SuiErrorKind::SignerSignatureAbsent { .. }
1157 | SuiErrorKind::SignerSignatureNumberMismatch { .. }
1158 | SuiErrorKind::IncorrectSigner { .. }
1159 | SuiErrorKind::UnknownSigner { .. }
1160 | SuiErrorKind::TransactionExpired => ErrorCategory::InvalidTransaction,
1161
1162 SuiErrorKind::ObjectLockConflict { .. } => ErrorCategory::LockConflict,
1163
1164 SuiErrorKind::Unknown { .. }
1165 | SuiErrorKind::GrpcMessageSerializeError { .. }
1166 | SuiErrorKind::GrpcMessageDeserializeError { .. }
1167 | SuiErrorKind::ByzantineAuthoritySuspicion { .. }
1168 | SuiErrorKind::InvalidTxKindInSoftBundle
1169 | SuiErrorKind::UnsupportedFeatureError { .. }
1170 | SuiErrorKind::InvalidRequest { .. } => ErrorCategory::Internal,
1171
1172 SuiErrorKind::TooManyTransactionsPendingExecution { .. }
1173 | SuiErrorKind::TooManyTransactionsPendingOnObject { .. }
1174 | SuiErrorKind::TooOldTransactionPendingOnObject { .. }
1175 | SuiErrorKind::TooManyTransactionsPendingConsensus
1176 | SuiErrorKind::TransactionRejectedDueToOutbiddingDuringCongestion { .. }
1177 | SuiErrorKind::ValidatorOverloadedRetryAfter { .. } => {
1178 ErrorCategory::ValidatorOverloaded
1179 }
1180
1181 SuiErrorKind::TimeoutError => ErrorCategory::Unavailable,
1182
1183 SuiErrorKind::StaggeredSubmissionNotProposer => ErrorCategory::Aborted,
1186
1187 _ => ErrorCategory::Aborted,
1189 }
1190 }
1191}
1192
1193impl Ord for SuiError {
1194 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
1195 Ord::cmp(self.as_ref(), other.as_ref())
1196 }
1197}
1198
1199impl PartialOrd for SuiError {
1200 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
1201 Some(self.cmp(other))
1202 }
1203}
1204
1205impl std::fmt::Debug for SuiError {
1206 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1207 self.as_inner().fmt(f)
1208 }
1209}
1210
1211pub(crate) type BoxError = Box<dyn std::error::Error + Send + Sync + 'static>;
1212pub type ExecutionErrorMetadata = BTreeMap<String, String>;
1213
1214pub trait ExecutionErrorTrait:
1216 From<ExecutionError> + Debug + std::error::Error + Send + Sync + Sized + 'static
1217{
1218 fn new(
1219 failure: ExecutionFailure,
1220 source: Option<BoxError>,
1221 metadata: ExecutionErrorMetadata,
1222 ) -> Self;
1223
1224 fn from_execution_failure(failure: ExecutionFailure) -> Self {
1225 Self::new(failure, None, ExecutionErrorMetadata::default())
1226 }
1227
1228 fn from_kind(kind: ExecutionErrorKind) -> Self {
1229 Self::from_execution_failure(ExecutionFailure::new(kind, None))
1230 }
1231
1232 fn new_with_source<E>(kind: ExecutionErrorKind, source: E) -> Self
1233 where
1234 E: Into<BoxError>,
1235 {
1236 Self::new(
1237 ExecutionFailure::new(kind, None),
1238 Some(source.into()),
1239 ExecutionErrorMetadata::default(),
1240 )
1241 }
1242
1243 fn with_command_index(self, command: CommandIndex) -> Self;
1244 fn kind(&self) -> &ExecutionErrorKind;
1245 fn command(&self) -> Option<CommandIndex>;
1246
1247 fn to_execution_failure(&self) -> ExecutionFailure {
1248 ExecutionFailure::new(self.kind().clone(), self.command())
1249 }
1250}
1251
1252#[derive(Debug)]
1253pub struct ExecutionError {
1254 inner: Box<ExecutionErrorInner>,
1255}
1256
1257#[derive(Debug)]
1258struct ExecutionErrorInner {
1259 kind: ExecutionErrorKind,
1260 source: Option<BoxError>,
1261 command: Option<CommandIndex>,
1262}
1263
1264impl ExecutionError {
1265 pub fn new(kind: ExecutionErrorKind, source: Option<BoxError>) -> Self {
1266 Self {
1267 inner: Box::new(ExecutionErrorInner {
1268 kind,
1269 source,
1270 command: None,
1271 }),
1272 }
1273 }
1274
1275 pub fn new_with_source<E: Into<BoxError>>(kind: ExecutionErrorKind, source: E) -> Self {
1276 Self::new(kind, Some(source.into()))
1277 }
1278
1279 pub fn invariant_violation<E: Into<BoxError>>(source: E) -> Self {
1280 Self::new_with_source(ExecutionErrorKind::InvariantViolation, source)
1281 }
1282
1283 pub fn with_command_index(mut self, command: CommandIndex) -> Self {
1284 self.inner.command = Some(command);
1285 self
1286 }
1287
1288 pub fn from_kind(kind: ExecutionErrorKind) -> Self {
1289 Self::new(kind, None)
1290 }
1291
1292 pub fn kind(&self) -> &ExecutionErrorKind {
1293 &self.inner.kind
1294 }
1295
1296 pub fn command(&self) -> Option<CommandIndex> {
1297 self.inner.command
1298 }
1299
1300 pub fn source(&self) -> &Option<BoxError> {
1301 &self.inner.source
1302 }
1303
1304 pub fn to_execution_status(&self) -> (ExecutionErrorKind, Option<CommandIndex>) {
1305 (self.kind().clone(), self.command())
1306 }
1307}
1308
1309impl ExecutionErrorTrait for ExecutionError {
1310 fn new(
1311 failure: ExecutionFailure,
1312 source: Option<BoxError>,
1313 _metadata: ExecutionErrorMetadata,
1314 ) -> Self {
1315 let ExecutionFailure { error, command } = failure;
1316 let err = ExecutionError::new(error, source);
1317 if let Some(command) = command {
1318 err.with_command_index(command)
1319 } else {
1320 err
1321 }
1322 }
1323
1324 fn with_command_index(self, command: CommandIndex) -> Self {
1325 self.with_command_index(command)
1326 }
1327
1328 fn kind(&self) -> &ExecutionErrorKind {
1329 self.kind()
1330 }
1331
1332 fn command(&self) -> Option<CommandIndex> {
1333 self.command()
1334 }
1335}
1336
1337#[derive(Debug)]
1338pub struct ExecutionErrorContext {
1339 kind: ExecutionErrorKind,
1340 metadata: ExecutionErrorMetadata,
1341 source: Option<BoxError>,
1342 command: Option<CommandIndex>,
1343}
1344
1345impl ExecutionErrorContext {
1346 pub fn kind(&self) -> &ExecutionErrorKind {
1347 &self.kind
1348 }
1349
1350 pub fn command(&self) -> Option<CommandIndex> {
1351 self.command
1352 }
1353
1354 pub fn metadata_with_source(&self) -> Option<ExecutionErrorMetadata> {
1355 let mut metadata = self.metadata.clone();
1356 if let Some(source) = self.source.as_ref() {
1357 metadata.insert("source".to_string(), source.to_string());
1358 }
1359
1360 (!metadata.is_empty()).then_some(metadata)
1361 }
1362
1363 pub fn to_execution_status(&self) -> (ExecutionErrorKind, Option<CommandIndex>) {
1364 (self.kind().clone(), self.command())
1365 }
1366}
1367
1368impl ExecutionErrorTrait for ExecutionErrorContext {
1369 fn new(
1370 failure: ExecutionFailure,
1371 source: Option<BoxError>,
1372 metadata: ExecutionErrorMetadata,
1373 ) -> Self {
1374 let ExecutionFailure { error, command } = failure;
1375 Self {
1376 kind: error,
1377 metadata,
1378 source,
1379 command,
1380 }
1381 }
1382
1383 fn with_command_index(self, command: CommandIndex) -> Self {
1384 Self {
1385 command: Some(command),
1386 ..self
1387 }
1388 }
1389
1390 fn kind(&self) -> &ExecutionErrorKind {
1391 self.kind()
1392 }
1393
1394 fn command(&self) -> Option<CommandIndex> {
1395 self.command()
1396 }
1397}
1398
1399impl std::fmt::Display for ExecutionErrorContext {
1400 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1401 write!(f, "ExecutionErrorContext: {:?}", self)
1402 }
1403}
1404
1405impl std::error::Error for ExecutionErrorContext {
1406 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
1407 self.source.as_deref().map(|e| e as _)
1408 }
1409}
1410
1411impl From<ExecutionErrorKind> for ExecutionErrorContext {
1412 fn from(kind: ExecutionErrorKind) -> Self {
1413 <Self as ExecutionErrorTrait>::from_kind(kind)
1414 }
1415}
1416
1417impl From<ExecutionFailure> for ExecutionErrorContext {
1418 fn from(value: ExecutionFailure) -> Self {
1419 <Self as ExecutionErrorTrait>::from_execution_failure(value)
1420 }
1421}
1422
1423impl From<ExecutionError> for ExecutionErrorContext {
1424 fn from(value: ExecutionError) -> Self {
1425 let ExecutionError { inner } = value;
1426 let ExecutionErrorInner {
1427 kind,
1428 source,
1429 command,
1430 } = *inner;
1431 Self {
1432 kind,
1433 metadata: BTreeMap::new(),
1434 source,
1435 command,
1436 }
1437 }
1438}
1439
1440impl From<ExecutionErrorContext> for ExecutionError {
1441 fn from(value: ExecutionErrorContext) -> Self {
1442 let ExecutionErrorContext {
1443 kind,
1444 metadata: _,
1445 source,
1446 command,
1447 } = value;
1448 let err = ExecutionError::new(kind, source);
1449 if let Some(command) = command {
1450 err.with_command_index(command)
1451 } else {
1452 err
1453 }
1454 }
1455}
1456
1457impl std::fmt::Display for ExecutionError {
1458 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1459 write!(f, "ExecutionError: {:?}", self)
1460 }
1461}
1462
1463impl std::error::Error for ExecutionError {
1464 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
1465 self.inner.source.as_ref().map(|e| &**e as _)
1466 }
1467}
1468
1469impl From<ExecutionErrorKind> for ExecutionError {
1470 fn from(kind: ExecutionErrorKind) -> Self {
1471 Self::from_kind(kind)
1472 }
1473}
1474
1475impl From<ExecutionFailure> for ExecutionError {
1476 fn from(value: ExecutionFailure) -> Self {
1477 <Self as ExecutionErrorTrait>::from_execution_failure(value)
1478 }
1479}
1480
1481pub fn command_argument_error(e: CommandArgumentError, arg_idx: usize) -> ExecutionError {
1482 ExecutionError::from_kind(ExecutionErrorKind::command_argument_error(
1483 e,
1484 arg_idx as u16,
1485 ))
1486}
1487
1488#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, IntoStaticStr)]
1490pub enum ErrorCategory {
1491 Aborted,
1493 InvalidTransaction,
1495 LockConflict,
1497 Internal,
1500 ValidatorOverloaded,
1502 Unavailable,
1504}
1505
1506impl ErrorCategory {
1507 pub fn is_submission_retriable(&self) -> bool {
1509 matches!(
1510 self,
1511 ErrorCategory::Aborted
1512 | ErrorCategory::ValidatorOverloaded
1513 | ErrorCategory::Unavailable
1514 )
1515 }
1516}