1use std::{collections::BTreeMap, convert::AsRef, fmt::Debug};
7
8use iota_sdk_types::{
9 Address, CheckpointContentsDigest, CommandArgumentError, ObjectDigest, ObjectId,
10 ObjectReference, Owner, TransactionDigest, TransactionEffectsDigest, Version,
11};
12use serde::{Deserialize, Serialize};
13use strum::{AsRefStr, IntoStaticStr};
14use thiserror::Error;
15#[cfg(not(target_arch = "wasm32"))]
16use tonic::Status;
17use typed_store_error::TypedStoreError;
18
19use crate::{
20 attestation::AuthorityIndex,
21 base_types::*,
22 committee::{Committee, EpochId, StakeUnit},
23 messages_checkpoint::CheckpointSequenceNumber,
24};
25
26#[cfg(test)]
27#[path = "unit_tests/error_codec_tests.rs"]
28mod error_codec_tests;
29
30pub const TRANSACTION_NOT_FOUND_MSG_PREFIX: &str = "Could not find the referenced transaction";
31pub const TRANSACTIONS_NOT_FOUND_MSG_PREFIX: &str = "Could not find the referenced transactions";
32
33#[macro_export]
34macro_rules! fp_bail {
35 ($e:expr) => {
36 return Err($e)
37 };
38}
39
40#[macro_export(local_inner_macros)]
41macro_rules! fp_ensure {
42 ($cond:expr, $e:expr) => {
43 if !($cond) {
44 fp_bail!($e);
45 }
46 };
47}
48
49use iota_sdk_types::ExecutionError as ExecutionFailureStatus;
50
51#[macro_export]
52macro_rules! exit_main {
53 ($result:expr) => {
54 match $result {
55 Ok(_) => (),
56 Err(err) => {
57 let err = format!("{:?}", err);
58 println!("{}", err.bold().red());
59 std::process::exit(1);
60 }
61 }
62 };
63}
64
65#[macro_export]
66macro_rules! make_invariant_violation {
67 ($($args:expr),* $(,)?) => {{
68 if cfg!(debug_assertions) {
69 panic!($($args),*)
70 }
71 ExecutionError::invariant_violation(format!($($args),*))
72 }}
73}
74
75#[macro_export]
76macro_rules! invariant_violation {
77 ($($args:expr),* $(,)?) => {
78 return Err(make_invariant_violation!($($args),*).into())
79 };
80}
81
82#[macro_export]
83macro_rules! assert_invariant {
84 ($cond:expr, $($args:expr),* $(,)?) => {{
85 if !$cond {
86 invariant_violation!($($args),*)
87 }
88 }};
89}
90
91#[cfg_attr(test, derive(iota_macros::EnumVariantOrder))]
97#[derive(
98 Eq, PartialEq, Clone, Debug, Serialize, Deserialize, Error, Hash, AsRefStr, IntoStaticStr,
99)]
100pub enum UserInputError {
101 #[error("Mutable object {object_id} cannot appear more than once in one transaction")]
102 MutableObjectUsedMoreThanOnce { object_id: ObjectId },
103 #[error("Wrong number of parameters for the transaction")]
104 ObjectInputArityViolation,
105 #[error("Could not find the referenced object {object_id} at version {version:?}")]
106 ObjectNotFound {
107 object_id: ObjectId,
108 version: Option<Version>,
109 },
110 #[error(
111 "Object ID {} Version {} Digest {} is not available for consumption, current version: {current_version}",
112 .provided_obj_ref.object_id, .provided_obj_ref.version, .provided_obj_ref.digest
113 )]
114 ObjectVersionUnavailableForConsumption {
115 provided_obj_ref: ObjectReference,
116 current_version: Version,
117 },
118 #[error("Package verification failed: {err}")]
119 PackageVerificationTimedout { err: String },
120 #[error("Dependent package not found on-chain: {package_id}")]
121 DependentPackageNotFound { package_id: ObjectId },
122 #[error("Mutable parameter provided, immutable parameter expected")]
123 ImmutableParameterExpected { object_id: ObjectId },
124 #[error("Size limit exceeded: {limit} is {value}")]
125 SizeLimitExceeded { limit: String, value: String },
126 #[error(
127 "Object {child_id} is owned by object {parent_id}. \
128 Objects owned by other objects cannot be used as input arguments"
129 )]
130 InvalidChildObjectArgument {
131 child_id: ObjectId,
132 parent_id: ObjectId,
133 },
134 #[error("Invalid Object digest for object {object_id}. Expected digest : {expected_digest}")]
135 InvalidObjectDigest {
136 object_id: ObjectId,
137 expected_digest: ObjectDigest,
138 },
139 #[error("Sequence numbers above the maximal value are not usable for transfers")]
140 InvalidSequenceNumber,
141 #[error("A move object is expected, instead a move package is passed: {object_id}")]
142 MovePackageAsObject { object_id: ObjectId },
143 #[error("A move package is expected, instead a move object is passed: {object_id}")]
144 MoveObjectAsPackage { object_id: ObjectId },
145 #[error("Transaction was not signed by the correct sender: {}", error)]
146 IncorrectUserSignature { error: String },
147
148 #[error("Object used as shared is not shared")]
149 NotSharedObject,
150 #[error("The transaction inputs contain duplicated ObjectReference's")]
151 DuplicateObjectRefInput,
152 #[error("A transaction input {object_id} is inconsistent")]
153 InconsistentInput { object_id: ObjectId },
154
155 #[error("Transaction gas payment missing")]
157 MissingGasPayment,
158 #[error("Gas object is not an owned object with owner: {}", owner)]
159 GasObjectNotOwnedObject { owner: Owner },
160 #[error("Gas budget: {} is higher than max: {}", gas_budget, max_budget)]
161 GasBudgetTooHigh { gas_budget: u64, max_budget: u64 },
162 #[error("Gas budget: {} is lower than min: {}", gas_budget, min_budget)]
163 GasBudgetTooLow { gas_budget: u64, min_budget: u64 },
164 #[error(
165 "Balance of gas object {} is lower than the needed amount: {}",
166 gas_balance,
167 needed_gas_amount
168 )]
169 GasBalanceTooLow {
170 gas_balance: u128,
171 needed_gas_amount: u128,
172 },
173 #[error("Transaction kind does not support Sponsored Transaction")]
174 UnsupportedSponsoredTransactionKind,
175 #[error(
176 "Gas price {} under reference gas price (RGP) {}",
177 gas_price,
178 reference_gas_price
179 )]
180 GasPriceUnderRGP {
181 gas_price: u64,
182 reference_gas_price: u64,
183 },
184 #[error("Gas price cannot exceed {} nanos", max_gas_price)]
185 GasPriceTooHigh { max_gas_price: u64 },
186 #[error("Object {object_id} is not a gas object")]
187 InvalidGasObject { object_id: ObjectId },
188 #[error("Gas object does not have enough balance to cover minimal gas spend")]
189 InsufficientBalanceToCoverMinimalGas,
190
191 #[error(
192 "Could not find the referenced object {} as the asked version {} is higher than the latest {}",
193 object_id,
194 asked_version,
195 latest_version
196 )]
197 ObjectSequenceNumberTooHigh {
198 object_id: ObjectId,
199 asked_version: Version,
200 latest_version: Version,
201 },
202 #[error("Object deleted at reference {:?}", object_ref)]
203 ObjectDeleted { object_ref: ObjectReference },
204 #[error("Invalid Batch Transaction: {}", error)]
205 InvalidBatchTransaction { error: String },
206 #[error("This Move function is currently disabled and not available for call")]
207 BlockedMoveFunction,
208 #[error("Empty input coins for Pay related transaction")]
209 EmptyInputCoins,
210 #[error("Invalid Move View Function call: {error}")]
211 InvalidMoveViewFunction { error: String },
212
213 #[error(
214 "IOTA payment transactions use first input coin for gas payment, but found a different gas object"
215 )]
216 UnexpectedGasPaymentObject,
217
218 #[error("Wrong initial version given for shared object")]
219 SharedObjectStartingVersionMismatch,
220
221 #[error("Wrong id given for shared object")]
222 SharedObjectIdMismatch,
223
224 #[error(
225 "Attempt to transfer object {object_id} that does not have public transfer. Object transfer must be done instead using a distinct Move function call"
226 )]
227 TransferObjectWithoutPublicTransfer { object_id: ObjectId },
228
229 #[error(
230 "TransferObjects, MergeCoin, and Publish cannot have empty arguments. \
231 If MakeMoveVec has empty arguments, it must have a type specified"
232 )]
233 EmptyCommandInput,
234
235 #[error("Transaction is denied: {error}")]
236 TransactionDenied { error: String },
237
238 #[error("Feature is not supported: {0}")]
239 Unsupported(String),
240
241 #[error("Query transactions with move function input error: {0}")]
242 MoveFunctionInput(String),
243
244 #[error("Verified checkpoint not found for sequence number: {0}")]
245 VerifiedCheckpointNotFound(CheckpointSequenceNumber),
246
247 #[error("Verified checkpoint not found for digest: {0}")]
248 VerifiedCheckpointDigestNotFound(String),
249
250 #[error("Latest checkpoint sequence number not found")]
251 LatestCheckpointSequenceNumberNotFound,
252
253 #[error("Checkpoint contents not found for digest: {0}")]
254 CheckpointContentsNotFound(CheckpointContentsDigest),
255
256 #[error("Genesis transaction not found")]
257 GenesisTransactionNotFound,
258
259 #[error("Transaction {0} not found")]
260 TransactionCursorNotFound(u64),
261
262 #[error("Object {object_id} is a system object and cannot be accessed by user transactions")]
263 InaccessibleSystemObject { object_id: ObjectId },
264 #[error(
265 "{max_publish_commands} max publish/upgrade commands allowed, {publish_count} provided"
266 )]
267 MaxPublishCountExceeded {
268 max_publish_commands: u64,
269 publish_count: u64,
270 },
271
272 #[error("Immutable parameter provided, mutable parameter expected for {object_id}")]
273 MutableParameterExpected { object_id: ObjectId },
274
275 #[error("Address {address} is denied for coin {coin_type}")]
276 AddressDeniedForCoin { address: Address, coin_type: String },
277
278 #[error("Commands following a command with Random can only be TransferObjects or MergeCoins")]
279 PostRandomCommandRestrictions,
280
281 #[error("Number of transactions exceeds the maximum allowed ({limit}) in a Soft Bundle")]
283 TooManyTransactionsInSoftBundle { limit: u64 },
284 #[error(
285 "Total transactions size ({size}) bytes exceeds the maximum allowed ({limit}) bytes in a Soft Bundle"
286 )]
287 SoftBundleTooLarge { size: u64, limit: u64 },
288 #[error("Transaction {} in Soft Bundle contains no shared objects", digest)]
289 NoSharedObject { digest: TransactionDigest },
290 #[error("Transaction {} in Soft Bundle has already been executed", digest)]
291 AlreadyExecuted { digest: TransactionDigest },
292 #[error("At least one certificate in Soft Bundle has already been processed")]
293 CertificateAlreadyProcessed,
294 #[error(
295 "Gas price for transaction {digest} in Soft Bundle mismatch: want {expected}, have {actual}"
296 )]
297 GasPriceMismatch {
298 digest: TransactionDigest,
299 expected: u64,
300 actual: u64,
301 },
302
303 #[error("Coin type is globally paused for use: {coin_type}")]
304 CoinTypeGlobalPause { coin_type: String },
305
306 #[error("Invalid identifier found in the transaction: {error}")]
307 InvalidIdentifier { error: String },
308
309 #[error(
311 "Account object {account_id} with version {account_version} was deleted in transaction {transaction_digest}"
312 )]
313 AccountObjectDeleted {
314 account_id: ObjectId,
315 account_version: Version,
316 transaction_digest: TransactionDigest,
317 },
318 #[error(
319 "Account object {account_id} with version {account_version} is used in a canceled transaction"
320 )]
321 AccountObjectInCanceledTransaction {
322 account_id: ObjectId,
323 account_version: Version,
324 },
325 #[error("Account object {object_id} is not a shared or immutable object that is unsupported")]
326 AccountObjectNotSupported { object_id: ObjectId },
327 #[error(
328 "The fetched account object version {actual_version} does not match the expected version {expected_version}, object id: {object_id}"
329 )]
330 AccountObjectVersionMismatch {
331 object_id: ObjectId,
332 expected_version: Version,
333 actual_version: Version,
334 },
335 #[error(
336 "The fetched account object digest {actual_digest} does not match the expected digest {expected_digest}, object id: {object_id}"
337 )]
338 InvalidAccountObjectDigest {
339 object_id: ObjectId,
340 expected_digest: ObjectDigest,
341 actual_digest: ObjectDigest,
342 },
343
344 #[error(
345 "AuthenticatorFunctionRef {authenticator_function_ref_id} not found for account {account_object_id} with version {account_object_version}"
346 )]
347 MoveAuthenticatorNotFound {
348 authenticator_function_ref_id: ObjectId,
349 account_object_id: ObjectId,
350 account_object_version: Version,
351 },
352 #[error("Unable to get a `MoveAuthenticator` object ID for account {account_object_id}")]
353 UnableToGetMoveAuthenticatorId { account_object_id: ObjectId },
354 #[error(
355 "Invalid authenticator function ref field value found for the account {account_object_id}"
356 )]
357 InvalidAuthenticatorFunctionRefField { account_object_id: ObjectId },
358
359 #[error("Package {package_id} is in the `MoveAuthenticator` input that is unsupported")]
360 PackageIsInMoveAuthenticatorInput { package_id: ObjectId },
361 #[error(
362 "Address-owned object {object_id} is in the `MoveAuthenticator` input that is unsupported"
363 )]
364 AddressOwnedIsInMoveAuthenticatorInput { object_id: ObjectId },
365 #[error(
366 "Object-owned object {object_id} is in the `MoveAuthenticator` input that is unsupported"
367 )]
368 ObjectOwnedIsInMoveAuthenticatorInput { object_id: ObjectId },
369 #[error(
370 "Mutable shared object {object_id} is in the `MoveAuthenticator` input that is unsupported"
371 )]
372 MutableSharedIsInMoveAuthenticatorInput { object_id: ObjectId },
373 #[error(
374 "Immutable account object {object_id} cannot authenticate a transaction, only a shared account object is supported"
375 )]
376 ImmutableAccountObjectNotSupported { object_id: ObjectId },
377 #[error(
378 "Randomness state object {object_id} is in the `MoveAuthenticator` input that is unsupported"
379 )]
380 RandomnessStateIsInMoveAuthenticatorInput { object_id: ObjectId },
381 #[error(
382 "Invalid argument at command {command_idx}, argument {argument_idx}: index {index} is out of bounds"
383 )]
384 InvalidArgumentIndex {
385 command_idx: usize,
386 argument_idx: usize,
387 index: u16,
388 },
389 #[error(
390 "Input object {object_id}{} can't be used: at the consensus commit that sequenced the transaction, it was already consumed, deleted, or didn't match what the transaction declared",
391 at_version(.version)
392 )]
393 InputObjectNotAvailableAtCommit {
394 object_id: ObjectId,
395 version: Option<Version>,
396 },
397}
398
399fn at_version(version: &Option<Version>) -> String {
402 version
403 .map(|version| format!(" at version {version}"))
404 .unwrap_or_default()
405}
406
407#[cfg_attr(test, derive(iota_macros::EnumVariantOrder))]
417#[derive(
418 Eq, PartialEq, Clone, Debug, Serialize, Deserialize, Error, Hash, AsRefStr, IntoStaticStr,
419)]
420pub enum IotaError {
421 #[error("Error checking transaction input objects: {error}")]
422 UserInput { error: UserInputError },
423
424 #[error("There are already {queue_len} transactions pending, above threshold of {threshold}")]
425 TooManyTransactionsPendingExecution { queue_len: usize, threshold: usize },
426
427 #[error("There are too many transactions pending in consensus")]
428 TooManyTransactionsPendingConsensus,
429
430 #[error(
431 "Input {object_id} already has {queue_len} transactions pending, above threshold of {threshold}"
432 )]
433 TooManyTransactionsPendingOnObject {
434 object_id: ObjectId,
435 queue_len: usize,
436 threshold: usize,
437 },
438
439 #[error(
440 "Input {object_id} has a transaction {txn_age_sec} seconds old pending, above threshold of {threshold} seconds"
441 )]
442 TooOldTransactionPendingOnObject {
443 object_id: ObjectId,
444 txn_age_sec: u64,
445 threshold: u64,
446 },
447
448 #[error("Soft bundle must only contain transactions of UserTransaction kind")]
449 InvalidTxKindInSoftBundle,
450
451 #[error("Signature is not valid: {}", error)]
453 InvalidSignature { error: String },
454 #[error("Required Signature from {expected} is absent {actual:?}")]
455 SignerSignatureAbsent {
456 expected: String,
457 actual: Vec<String>,
458 },
459 #[error("Expect {expected} signer signatures but got {actual}")]
460 SignerSignatureNumberMismatch { expected: usize, actual: usize },
461 #[error("Value was not signed by the correct sender: {}", error)]
462 IncorrectSigner { error: String },
463 #[error(
464 "Value was not signed by a known authority. signer: {:?}, index: {:?}, committee: {committee}",
465 signer,
466 index
467 )]
468 UnknownSigner {
469 signer: Option<String>,
470 index: Option<u32>,
471 committee: Box<Committee>,
472 },
473 #[error(
474 "Validator {signer:?} responded multiple signatures for the same message, conflicting: {conflicting_sig}"
475 )]
476 StakeAggregatorRepeatedSigner {
477 signer: AuthorityName,
478 conflicting_sig: bool,
479 },
480 #[error("Signature is not valid, but a retry may result in a valid one: {error}")]
483 PotentiallyTemporarilyInvalidSignature { error: String },
484
485 #[error(
487 "Signature or certificate from wrong epoch, expected {expected_epoch}, got {actual_epoch}"
488 )]
489 WrongEpoch {
490 expected_epoch: EpochId,
491 actual_epoch: EpochId,
492 },
493 #[error("Signatures in a certificate must form a quorum")]
494 CertificateRequiresQuorum,
495 #[error("Transaction certificate processing failed: {err}")]
496 ErrorWhileProcessingCertificate { err: String },
498 #[error(
499 "Failed to get a quorum of signed effects when processing transaction: {effects_map:?}"
500 )]
501 QuorumFailedToGetEffectsQuorumWhenProcessingTransaction {
502 effects_map: BTreeMap<TransactionEffectsDigest, (Vec<AuthorityName>, StakeUnit)>,
503 },
504 #[error(
505 "Failed to verify Tx certificate with executed effects, error: {error}, validator: {validator_name:?}"
506 )]
507 FailedToVerifyTxCertWithExecutedEffects {
508 validator_name: AuthorityName,
509 error: String,
510 },
511 #[error("Transaction is already finalized but with different user signatures")]
512 TxAlreadyFinalizedWithDifferentUserSigs,
513
514 #[error("Invalid authenticator")]
516 InvalidAuthenticator,
517 #[error("Invalid address")]
518 InvalidAddress,
519 #[error("Invalid transaction digest")]
520 InvalidTransactionDigest,
521 #[error("Invalid move authentication digest")]
522 InvalidMoveAuthenticatorDigest,
523
524 #[error("Invalid digest length. Expected {expected}, got {actual}")]
525 InvalidDigestLength { expected: usize, actual: usize },
526 #[error("Invalid DKG message size")]
527 InvalidDkgMessageSize,
528
529 #[error("Unexpected message")]
530 UnexpectedMessage,
531
532 #[error("Failed to execute the Move authenticator, reason: {error}")]
533 MoveAuthenticatorExecutionFailure { error: String },
534
535 #[error("Failed to verify the Move module, reason: {error}")]
537 ModuleVerificationFailure { error: String },
538 #[error("Failed to deserialize the Move module, reason: {error}")]
539 ModuleDeserializationFailure { error: String },
540 #[error("Failed to publish the Move module(s), reason: {error}")]
541 ModulePublishFailure { error: String },
542 #[error("Failed to build Move modules: {error}")]
543 ModuleBuildFailure { error: String },
544
545 #[error("Function resolution failure: {error}")]
547 FunctionNotFound { error: String },
548 #[error("Module not found in package: {module_name:?}")]
549 ModuleNotFound { module_name: String },
550 #[error("Type error while binding function arguments: {error}")]
551 Type { error: String },
552 #[error("Circular object ownership detected")]
553 CircularObjectOwnership,
554
555 #[error("Attempt to re-initialize a transaction lock for objects {refs:?}")]
557 ObjectLockAlreadyInitialized { refs: Vec<ObjectReference> },
558 #[error("Object {obj_ref:?} already locked by a different transaction: {pending_transaction}")]
559 ObjectLockConflict {
560 obj_ref: ObjectReference,
561 pending_transaction: TransactionDigest,
562 },
563 #[error(
564 "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:?}"
565 )]
566 ObjectLockedAtFutureEpoch {
567 obj_refs: Vec<ObjectReference>,
568 locked_epoch: EpochId,
569 new_epoch: EpochId,
570 locked_by_tx: TransactionDigest,
571 },
572 #[error("Transaction {digest:?} was recently submitted; duplicate resubmission suppressed")]
573 RecentlyResubmitted { digest: TransactionDigest },
574 #[error("{TRANSACTION_NOT_FOUND_MSG_PREFIX} [{digest}]")]
575 TransactionNotFound { digest: TransactionDigest },
576 #[error("{TRANSACTIONS_NOT_FOUND_MSG_PREFIX} [{digests:?}]")]
577 TransactionsNotFound { digests: Vec<TransactionDigest> },
578 #[error("Could not find the referenced transaction events [{digest}]")]
579 TransactionEventsNotFound { digest: TransactionDigest },
580 #[error(
581 "Attempt to move to `Executed` state an transaction that has already been executed: {digest}"
582 )]
583 TransactionAlreadyExecuted { digest: TransactionDigest },
584 #[error("Object ID did not have the expected type")]
585 BadObjectType { error: String },
586 #[error("Fail to retrieve Object layout for {st}")]
587 FailObjectLayout { st: String },
588
589 #[error("Execution invariant violated")]
590 ExecutionInvariantViolation,
591 #[error("Validator {authority:?} is faulty in a Byzantine manner: {reason}")]
592 ByzantineAuthoritySuspicion {
593 authority: AuthorityName,
594 reason: String,
595 },
596 #[error(
597 "Attempted to access {object} through parent {given_parent}, \
598 but it's actual parent is {actual_owner}"
599 )]
600 InvalidChildObjectAccess {
601 object: ObjectId,
602 given_parent: ObjectId,
603 actual_owner: Owner,
604 },
605
606 #[error("Authority Error: {error}")]
607 GenericAuthority { error: String },
608
609 #[error("Failed to dispatch subscription: {error}")]
610 FailedToDispatchSubscription { error: String },
611
612 #[error("Failed to serialize Owner: {error}")]
613 OwnerFailedToSerialize { error: String },
614
615 #[error("Failed to deserialize fields into JSON: {error}")]
616 ExtraFieldFailedToDeserialize { error: String },
617
618 #[error("Failed to execute transaction locally by Orchestrator: {error}")]
619 TransactionOrchestratorLocalExecution { error: String },
620
621 #[error("Failure serializing transaction in the requested format: {error}")]
623 TransactionSerialization { error: String },
624 #[error("Failure serializing object in the requested format: {error}")]
625 ObjectSerialization { error: String },
626 #[error("Failure deserializing object in the requested format: {error}")]
627 ObjectDeserialization { error: String },
628 #[error("Failure deserializing runtime module metadata in the requested format: {error}")]
629 RuntimeModuleMetadataDeserialization { error: String },
630 #[error("Event store component is not active on this node")]
631 NoEventStore,
632
633 #[error("Too many authority errors were detected for {action}: {errors:?}")]
635 TooManyIncorrectAuthorities {
636 errors: Vec<(AuthorityName, IotaError)>,
637 action: String,
638 },
639 #[error("Invalid transaction range query to the fullnode: {error}")]
640 FullNodeInvalidTxRangeQuery { error: String },
641
642 #[error("Failed to submit transaction to consensus: {0}")]
644 FailedToSubmitToConsensus(String),
645 #[error("Failed to connect with consensus node: {0}")]
646 ConsensusConnectionBroken(String),
647 #[error("Failed to execute handle_consensus_transaction on Iota: {0}")]
648 HandleConsensusTransactionFailure(String),
649
650 #[error("Signature key generation error: {0}")]
652 SignatureKeyGen(String),
653 #[error("Key Conversion Error: {0}")]
654 KeyConversion(String),
655 #[error("Invalid Private Key provided")]
656 InvalidPrivateKey,
657
658 #[error("Fullnode does not support handle_certificate")]
660 FullNodeCantHandleCertificate,
661 #[error("Fullnode does not support ValidatorV2 endpoints")]
662 FullNodeCantHandleValidatorV2,
663 #[error("Fullnode does not support handle_authority_capabilities")]
664 FullNodeCantHandleAuthorityCapabilities,
665
666 #[error("Validator temporarily stopped processing transactions due to epoch change")]
668 ValidatorHaltedAtEpochEnd,
669 #[error("Operations for epoch {0} have ended")]
670 EpochEnded(EpochId),
671 #[error("Error when advancing epoch: {error}")]
672 AdvanceEpoch { error: String },
673
674 #[error("Transaction Expired")]
675 TransactionExpired,
676
677 #[error("{1} - {0}")]
680 Rpc(String, String),
681
682 #[error("Method not allowed")]
683 InvalidRpcMethod,
684
685 #[error("Use of disabled feature: {error}")]
687 UnsupportedFeature { error: String },
688
689 #[error("Unable to communicate with the Quorum Driver channel: {error}")]
690 QuorumDriverCommunication { error: String },
691
692 #[error("Operation timed out")]
693 Timeout,
694
695 #[error("Error executing {0}")]
696 Execution(String),
697
698 #[error("Invalid committee composition")]
699 InvalidCommittee(String),
700
701 #[error("Missing committee information for epoch {0}")]
702 MissingCommitteeAtEpoch(EpochId),
703
704 #[error("Index store not available on this Fullnode")]
705 IndexStoreNotAvailable,
706
707 #[error("Failed to read dynamic field from table in the object store: {0}")]
708 DynamicFieldRead(String),
709
710 #[error("Failed to read or deserialize system state related data structures on-chain: {0}")]
711 IotaSystemStateRead(String),
712
713 #[error("Unexpected version error: {0}")]
714 UnexpectedVersion(String),
715
716 #[error("Message version is not supported at the current protocol version: {error}")]
717 WrongMessageVersion { error: String },
718
719 #[error("unknown error: {0}")]
720 Unknown(String),
721
722 #[error("Failed to perform file operation: {0}")]
723 FileIO(String),
724
725 #[error("Failed to get JWK")]
726 JWKRetrieval,
727
728 #[error("Storage error: {0}")]
729 Storage(String),
730
731 #[error(
732 "Validator cannot handle the request at the moment. Please retry after at least {retry_after_secs} seconds"
733 )]
734 ValidatorOverloadedRetryAfter { retry_after_secs: u64 },
735
736 #[error("Too many requests")]
737 TooManyRequests,
738
739 #[error("The request did not contain a certificate")]
740 NoCertificateProvided,
741
742 #[error("Invalid admin request: {0}")]
743 InvalidAdminRequest(String),
744
745 #[error("Could not find the referenced transaction effects [{digest}]")]
746 TransactionEffectsNotFound { digest: TransactionDigest },
747
748 #[error("Dynamic field with key={key} and ID={id} does not exist on parent {parent_id}")]
749 DynamicFieldNotExists {
750 parent_id: ObjectId,
751 id: ObjectId,
752 key: String,
753 },
754
755 #[error(
756 "Attestation author mismatch: attestation claims attestor {expected:?} \
757 but the block was authored by {actual:?}"
758 )]
759 AttestationAuthorMismatch {
760 expected: AuthorityIndex,
761 actual: AuthorityIndex,
762 },
763
764 #[error(
765 "Attestation reports computation_units = {actual} gas units, \
766 below the protocol minimum of {minimum} \
767 (`min(base_tx_cost_fixed, gas_rounding_step)`); \
768 no honest dry-run can meter below this minimum"
769 )]
770 AttestationUnitsBelowMinimum { actual: u64, minimum: u64 },
771
772 #[error(
773 "Attestation reports computation_units = {actual} gas units, \
774 above the user's budget maximum of {maximum} (`gas_budget / gas_price`); \
775 an honest dry-run cannot exceed what the tx can pay for"
776 )]
777 AttestationUnitsAboveBudget { actual: u64, maximum: u64 },
778
779 #[error(
780 "Attestor {attestor:?} is cut off for the rest of the epoch: the squared \
781 units of its refuted attestations reached \
782 `attestation_max_refuted_units_squared`"
783 )]
784 AttestorCutOff { attestor: AuthorityIndex },
785}
786
787#[repr(u64)]
788#[expect(non_camel_case_types)]
789#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
790pub enum VMMVerifierErrorSubStatusCode {
794 MULTIPLE_RETURN_VALUES_NOT_ALLOWED = 0,
795 INVALID_OBJECT_CREATION = 1,
796}
797
798#[repr(u64)]
799#[expect(non_camel_case_types)]
800#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
801pub enum VMMemoryLimitExceededSubStatusCode {
804 EVENT_COUNT_LIMIT_EXCEEDED = 0,
805 EVENT_SIZE_LIMIT_EXCEEDED = 1,
806 NEW_ID_COUNT_LIMIT_EXCEEDED = 2,
807 DELETED_ID_COUNT_LIMIT_EXCEEDED = 3,
808 TRANSFER_ID_COUNT_LIMIT_EXCEEDED = 4,
809 OBJECT_RUNTIME_CACHE_LIMIT_EXCEEDED = 5,
810 OBJECT_RUNTIME_STORE_LIMIT_EXCEEDED = 6,
811 TOTAL_EVENT_SIZE_LIMIT_EXCEEDED = 7,
812}
813
814pub type IotaResult<T = ()> = Result<T, IotaError>;
815pub type UserInputResult<T = ()> = Result<T, UserInputError>;
816
817impl From<iota_protocol_config::Error> for IotaError {
818 fn from(error: iota_protocol_config::Error) -> Self {
819 IotaError::WrongMessageVersion { error: error.0 }
820 }
821}
822
823impl From<ExecutionError> for IotaError {
824 fn from(error: ExecutionError) -> Self {
825 IotaError::Execution(error.to_string())
826 }
827}
828
829impl From<iota_sdk_types::hash::MissingSignatureError> for IotaError {
830 fn from(error: iota_sdk_types::hash::MissingSignatureError) -> Self {
831 IotaError::InvalidSignature {
832 error: error.to_string(),
833 }
834 }
835}
836
837#[cfg(not(target_arch = "wasm32"))]
838impl From<Status> for IotaError {
839 fn from(status: Status) -> Self {
840 if status.message() == "Too many requests" {
841 return Self::TooManyRequests;
842 }
843 let result = bcs::from_bytes::<IotaError>(status.details());
844 if let Ok(iota_error) = result {
845 iota_error
846 } else {
847 Self::Rpc(
848 status.message().to_owned(),
849 status.code().description().to_owned(),
850 )
851 }
852 }
853}
854
855impl From<TypedStoreError> for IotaError {
856 fn from(e: TypedStoreError) -> Self {
857 Self::Storage(e.to_string())
858 }
859}
860
861impl From<crate::storage::error::Error> for IotaError {
862 fn from(e: crate::storage::error::Error) -> Self {
863 Self::Storage(e.to_string())
864 }
865}
866
867#[cfg(not(target_arch = "wasm32"))]
868impl From<IotaError> for Status {
869 fn from(error: IotaError) -> Self {
870 let bytes = bcs::to_bytes(&error).unwrap();
871 Status::with_details(tonic::Code::Internal, error.to_string(), bytes.into())
872 }
873}
874
875impl From<ExecutionErrorKind> for IotaError {
876 fn from(kind: ExecutionErrorKind) -> Self {
877 ExecutionError::from_kind(kind).into()
878 }
879}
880
881impl From<&str> for IotaError {
882 fn from(error: &str) -> Self {
883 IotaError::GenericAuthority {
884 error: error.to_string(),
885 }
886 }
887}
888
889impl From<String> for IotaError {
890 fn from(error: String) -> Self {
891 IotaError::GenericAuthority { error }
892 }
893}
894
895impl TryFrom<IotaError> for UserInputError {
896 type Error = anyhow::Error;
897
898 fn try_from(err: IotaError) -> Result<Self, Self::Error> {
899 match err {
900 IotaError::UserInput { error } => Ok(error),
901 other => anyhow::bail!("error `{other}` is not UserInput"),
902 }
903 }
904}
905
906impl From<UserInputError> for IotaError {
907 fn from(error: UserInputError) -> Self {
908 IotaError::UserInput { error }
909 }
910}
911
912impl IotaError {
913 pub fn individual_error_indicates_epoch_change(&self) -> bool {
914 matches!(
915 self,
916 IotaError::ValidatorHaltedAtEpochEnd | IotaError::MissingCommitteeAtEpoch(_)
917 )
918 }
919
920 pub fn is_retryable(&self) -> (bool, bool) {
926 let retryable = match self {
927 IotaError::Rpc { .. } => true,
928
929 IotaError::ValidatorHaltedAtEpochEnd => true,
931 IotaError::MissingCommitteeAtEpoch(..) => true,
932 IotaError::WrongEpoch { .. } => true,
933 IotaError::EpochEnded { .. } => true,
934
935 IotaError::UserInput { error } => {
936 match error {
937 UserInputError::ObjectNotFound { .. } => true,
939 UserInputError::DependentPackageNotFound { .. } => true,
940 UserInputError::InputObjectNotAvailableAtCommit { .. } => false,
943 _ => false,
944 }
945 }
946
947 IotaError::PotentiallyTemporarilyInvalidSignature { .. } => true,
948
949 IotaError::TooManyTransactionsPendingExecution { .. } => true,
951 IotaError::TooManyTransactionsPendingOnObject { .. } => true,
952 IotaError::TooOldTransactionPendingOnObject { .. } => true,
953 IotaError::TooManyTransactionsPendingConsensus => true,
954 IotaError::ValidatorOverloadedRetryAfter { .. } => true,
955
956 IotaError::FailedToSubmitToConsensus(..) => true,
958
959 IotaError::RecentlyResubmitted { .. } => true,
962
963 IotaError::Execution(..) => false,
965 IotaError::ByzantineAuthoritySuspicion { .. } => false,
966 IotaError::QuorumFailedToGetEffectsQuorumWhenProcessingTransaction { .. } => false,
967 IotaError::TxAlreadyFinalizedWithDifferentUserSigs => false,
968 IotaError::FailedToVerifyTxCertWithExecutedEffects { .. } => false,
969 IotaError::ObjectLockConflict { .. } => false,
970
971 IotaError::TooManyRequests => false,
975
976 IotaError::InvalidSignature { .. } => false,
978 IotaError::SignerSignatureAbsent { .. } => false,
979 IotaError::SignerSignatureNumberMismatch { .. } => false,
980 IotaError::IncorrectSigner { .. } => false,
981 IotaError::UnknownSigner { .. } => false,
982 IotaError::InvalidAuthenticator => false,
983
984 IotaError::TransactionExpired => false,
986
987 IotaError::StakeAggregatorRepeatedSigner { .. } => false,
989 IotaError::CertificateRequiresQuorum => false,
990
991 _ => return (false, false),
993 };
994
995 (retryable, true)
996 }
997
998 pub fn is_object_or_package_not_found(&self) -> bool {
999 match self {
1000 IotaError::UserInput { error } => {
1001 matches!(
1002 error,
1003 UserInputError::ObjectNotFound { .. }
1004 | UserInputError::DependentPackageNotFound { .. }
1005 )
1006 }
1007 _ => false,
1008 }
1009 }
1010
1011 pub fn is_overload(&self) -> bool {
1012 matches!(
1013 self,
1014 IotaError::TooManyTransactionsPendingExecution { .. }
1015 | IotaError::TooManyTransactionsPendingOnObject { .. }
1016 | IotaError::TooOldTransactionPendingOnObject { .. }
1017 | IotaError::TooManyTransactionsPendingConsensus
1018 )
1019 }
1020
1021 pub fn is_retryable_overload(&self) -> bool {
1022 matches!(self, IotaError::ValidatorOverloadedRetryAfter { .. })
1023 }
1024
1025 pub fn is_storage_or_epoch_error(&self) -> bool {
1030 matches!(
1031 self,
1032 IotaError::Storage(..)
1033 | IotaError::EpochEnded(..)
1034 | IotaError::ValidatorHaltedAtEpochEnd
1035 )
1036 }
1037
1038 pub fn retry_after_secs(&self) -> u64 {
1039 match self {
1040 IotaError::ValidatorOverloadedRetryAfter { retry_after_secs } => *retry_after_secs,
1041 _ => 0,
1042 }
1043 }
1044}
1045
1046pub fn categorize(error: &IotaError) -> ErrorCategory {
1048 match error {
1049 IotaError::UserInput { error } => match error {
1050 UserInputError::ObjectNotFound { .. } => ErrorCategory::Aborted,
1051 UserInputError::DependentPackageNotFound { .. } => ErrorCategory::Aborted,
1052 UserInputError::InputObjectNotAvailableAtCommit { .. } => {
1053 ErrorCategory::InvalidTransaction
1054 }
1055 _ => ErrorCategory::InvalidTransaction,
1056 },
1057 IotaError::InvalidSignature { .. }
1058 | IotaError::SignerSignatureAbsent { .. }
1059 | IotaError::SignerSignatureNumberMismatch { .. }
1060 | IotaError::IncorrectSigner { .. }
1061 | IotaError::UnknownSigner { .. }
1062 | IotaError::TransactionExpired => ErrorCategory::InvalidTransaction,
1063
1064 IotaError::ObjectLockConflict { .. } => ErrorCategory::LockConflict,
1065
1066 IotaError::TooManyTransactionsPendingExecution { .. }
1067 | IotaError::TooManyTransactionsPendingOnObject { .. }
1068 | IotaError::TooOldTransactionPendingOnObject { .. }
1069 | IotaError::TooManyTransactionsPendingConsensus
1070 | IotaError::ValidatorOverloadedRetryAfter { .. } => ErrorCategory::ValidatorOverloaded,
1071
1072 _ => ErrorCategory::Aborted,
1073 }
1074}
1075
1076#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, IntoStaticStr)]
1078pub enum ErrorCategory {
1079 Aborted,
1081 InvalidTransaction,
1083 LockConflict,
1085 Internal,
1089 ValidatorOverloaded,
1091 Unavailable,
1093}
1094
1095impl ErrorCategory {
1096 pub fn is_submission_retriable(&self) -> bool {
1098 matches!(
1099 self,
1100 ErrorCategory::Aborted
1101 | ErrorCategory::ValidatorOverloaded
1102 | ErrorCategory::Unavailable
1103 )
1104 }
1105}
1106
1107impl IotaError {
1108 pub fn categorize(&self) -> ErrorCategory {
1110 categorize(self)
1111 }
1112}
1113
1114impl Ord for IotaError {
1115 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
1116 Ord::cmp(self.as_ref(), other.as_ref())
1117 }
1118}
1119
1120impl PartialOrd for IotaError {
1121 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
1122 Some(self.cmp(other))
1123 }
1124}
1125
1126type BoxError = Box<dyn std::error::Error + Send + Sync + 'static>;
1127
1128pub type ExecutionErrorKind = ExecutionFailureStatus;
1129
1130#[derive(Debug)]
1131pub struct ExecutionError {
1132 inner: Box<ExecutionErrorInner>,
1133}
1134
1135#[derive(Debug)]
1136struct ExecutionErrorInner {
1137 kind: ExecutionErrorKind,
1138 source: Option<BoxError>,
1139 command: Option<u64>,
1140}
1141
1142impl ExecutionError {
1143 pub fn new(kind: ExecutionErrorKind, source: Option<BoxError>) -> Self {
1144 Self {
1145 inner: Box::new(ExecutionErrorInner {
1146 kind,
1147 source,
1148 command: None,
1149 }),
1150 }
1151 }
1152
1153 pub fn new_with_source<E: Into<BoxError>>(kind: ExecutionErrorKind, source: E) -> Self {
1154 Self::new(kind, Some(source.into()))
1155 }
1156
1157 pub fn invariant_violation<E: Into<BoxError>>(source: E) -> Self {
1158 Self::new_with_source(ExecutionFailureStatus::InvariantViolation, source)
1159 }
1160
1161 pub fn with_command_index(mut self, command: u64) -> Self {
1162 self.inner.command = Some(command);
1163 self
1164 }
1165
1166 pub fn into_move_authentication_error(self) -> Self {
1173 let ExecutionErrorInner { kind, source, .. } = *self.inner;
1174 Self::new(
1175 ExecutionFailureStatus::MoveAuthentication {
1176 error: Box::new(kind),
1177 },
1178 source,
1179 )
1180 }
1181
1182 pub fn from_kind(kind: ExecutionErrorKind) -> Self {
1183 Self::new(kind, None)
1184 }
1185
1186 pub fn kind(&self) -> &ExecutionErrorKind {
1187 &self.inner.kind
1188 }
1189
1190 pub fn command(&self) -> Option<u64> {
1191 self.inner.command
1192 }
1193
1194 pub fn source(&self) -> &Option<BoxError> {
1195 &self.inner.source
1196 }
1197
1198 pub fn to_execution_status(&self) -> (ExecutionFailureStatus, Option<u64>) {
1199 (self.kind().clone(), self.command())
1200 }
1201}
1202
1203impl std::fmt::Display for ExecutionError {
1204 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1205 write!(f, "{}: {}", self.inner.kind.as_ref(), self.inner.kind)?;
1206 if let Some(source) = self.inner.source.as_ref() {
1207 write!(f, "; caused by: {source}")?;
1208 }
1209 if let Some(command) = self.inner.command {
1210 write!(f, "; at command index: {command}")?;
1211 }
1212 Ok(())
1213 }
1214}
1215
1216impl std::error::Error for ExecutionError {
1217 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
1218 self.inner.source.as_ref().map(|e| &**e as _)
1219 }
1220}
1221
1222impl From<ExecutionErrorKind> for ExecutionError {
1223 fn from(kind: ExecutionErrorKind) -> Self {
1224 Self::from_kind(kind)
1225 }
1226}
1227
1228pub fn command_argument_error(e: CommandArgumentError, arg_idx: usize) -> ExecutionError {
1229 ExecutionError::from_kind(ExecutionErrorKind::command_argument_error(
1230 e,
1231 arg_idx as u16,
1232 ))
1233}