1use thiserror::Error;
6
7use super::{Address, Digest, Identifier, ObjectId};
8
9#[derive(Clone, Debug, Eq, PartialEq)]
21#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))]
22#[cfg_attr(feature = "bcs-schema", derive(iota_bcs_schema::BcsSchema))]
23#[non_exhaustive]
24pub enum ExecutionStatus {
25 Success,
27 Failure {
34 error: ExecutionError,
36 #[cfg_attr(feature = "proptest", map(|x: Option<u16>| x.map(Into::into)))]
38 command: Option<u64>,
39 },
40}
41
42impl ExecutionStatus {
43 crate::def_is!(Success, Failure);
44
45 pub fn new_failure(error: ExecutionError, command: Option<u64>) -> Self {
46 Self::Failure { error, command }
47 }
48
49 pub fn unwrap(&self) {
50 match self {
51 Self::Success => {}
52 Self::Failure { .. } => {
53 panic!("Unable to unwrap() on {self:?}");
54 }
55 }
56 }
57
58 pub fn unwrap_err(self) -> (ExecutionError, Option<u64>) {
59 match self {
60 Self::Success => {
61 panic!("Unable to unwrap_err() on {self:?}");
62 }
63 Self::Failure { error, command } => (error, command),
64 }
65 }
66
67 pub fn error(&self) -> Option<&ExecutionError> {
69 if let Self::Failure { error, .. } = self {
70 Some(error)
71 } else {
72 None
73 }
74 }
75
76 pub fn error_command(&self) -> Option<u64> {
78 if let Self::Failure { command, .. } = self {
79 *command
80 } else {
81 None
82 }
83 }
84}
85
86impl crate::TreeDisplay for ExecutionStatus {
87 fn fmt_tree(&self, w: &mut crate::TreeWriter<'_, '_>) -> std::fmt::Result {
88 w.enum_name("Execution Status");
89 match self {
90 ExecutionStatus::Success => w.header("Success"),
91 ExecutionStatus::Failure { error, command } => {
92 w.header("Failure")?;
93 w.leaf("Error", error, false)?;
94 w.option_leaf("Command", command, true)
95 }
96 }
97 }
98}
99
100crate::impl_tree_display!(ExecutionStatus);
101
102fn display_move_location_opt(location: &Option<MoveLocation>) -> impl core::fmt::Display + '_ {
103 struct W<'a>(&'a Option<MoveLocation>);
104 impl core::fmt::Display for W<'_> {
105 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
106 match &self.0 {
107 None => write!(f, "UNKNOWN"),
108 Some(l) => write!(f, "{l}"),
109 }
110 }
111 }
112 W(location)
113}
114
115fn display_congested_objects(objects: &[ObjectId]) -> impl core::fmt::Display + '_ {
116 struct W<'a>(&'a [ObjectId]);
117 impl core::fmt::Display for W<'_> {
118 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
119 let mut iter = self.0.iter();
120 if let Some(first) = iter.next() {
121 write!(f, "{first}")?;
122 for obj in iter {
123 write!(f, ", {obj}")?;
124 }
125 }
126 Ok(())
127 }
128 }
129 W(objects)
130}
131
132#[derive(Clone, Debug, Eq, Error, PartialEq, strum::AsRefStr)]
230#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
231#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))]
232#[cfg_attr(feature = "bcs-schema", derive(iota_bcs_schema::BcsSchema))]
233#[non_exhaustive]
234pub enum ExecutionError {
235 #[error("Insufficient Gas")]
237 InsufficientGas,
238 #[error("Invalid Gas Object. Possibly not address-owned or possibly not an IOTA coin")]
240 InvalidGasObject,
241 #[error("INVARIANT VIOLATION")]
243 InvariantViolation,
244 #[error("Attempted to use feature that is not supported yet")]
246 FeatureNotYetSupported,
247 #[error(
249 "Move object with size {object_size} is larger than the maximum object size {max_object_size}"
250 )]
251 ObjectTooBig {
252 #[cfg_attr(feature = "serde", serde(with = "crate::_serde::ReadableDisplay"))]
253 object_size: u64,
254 #[cfg_attr(feature = "serde", serde(with = "crate::_serde::ReadableDisplay"))]
255 max_object_size: u64,
256 },
257 #[error(
259 "Move package with size {object_size} is larger than the maximum object size {max_object_size}"
260 )]
261 PackageTooBig {
262 #[cfg_attr(feature = "serde", serde(with = "crate::_serde::ReadableDisplay"))]
263 object_size: u64,
264 #[cfg_attr(feature = "serde", serde(with = "crate::_serde::ReadableDisplay"))]
265 max_object_size: u64,
266 },
267 #[error("Circular Object Ownership, including object {object}")]
269 CircularObjectOwnership { object: ObjectId },
270 #[error("Insufficient coin balance for operation")]
272 InsufficientCoinBalance,
273 #[error("The coin balance overflows u64")]
275 CoinBalanceOverflow,
276 #[error(
279 "Publish Error, Non-zero Address. The modules in the package must have their self-addresses set to zero."
280 )]
281 PublishErrorNonZeroAddress,
282 #[error(
284 "IOTA Move Bytecode Verification Error. Please run the IOTA Move Verifier for more information."
285 )]
286 IotaMoveVerificationError,
287 #[error(
291 "Move Primitive Runtime Error. Location: {}. Arithmetic error, stack overflow, max value depth, etc.",
292 display_move_location_opt(.location)
293 )]
294 MovePrimitiveRuntimeError { location: Option<MoveLocation> },
295 #[error("Move Runtime Abort. Location: {location}, Abort Code: {code}")]
297 MoveAbort {
298 location: MoveLocation,
299 #[cfg_attr(feature = "serde", serde(with = "crate::_serde::ReadableDisplay"))]
300 code: u64,
301 },
302 #[error(
304 "Move Bytecode Verification Error. Please run the Bytecode Verifier for more information."
305 )]
306 VmVerificationOrDeserializationError,
307 #[error("MOVE VM INVARIANT VIOLATION")]
309 VmInvariantViolation,
310 #[error("Function Not Found")]
312 FunctionNotFound,
313 #[error(
316 "Arity mismatch for Move function. The number of arguments does not match the number of parameters"
317 )]
318 ArityMismatch,
319 #[error(
322 "Type arity mismatch for Move function. Mismatch between the number of actual versus expected type arguments."
323 )]
324 TypeArityMismatch,
325 #[error("Non Entry Function Invoked. Move Call must start with an entry function")]
327 NonEntryFunctionInvoked,
328 #[error("Invalid command argument at {argument}. {kind}")]
330 CommandArgumentError {
331 argument: u16,
332 kind: CommandArgumentError,
333 },
334 #[error("Error for type argument at index {type_argument}: {kind}")]
336 TypeArgumentError {
337 type_argument: u16,
339 kind: TypeArgumentError,
340 },
341 #[error(
343 "Unused result without the drop ability. Command result {result}, return value {subresult}"
344 )]
345 UnusedValueWithoutDrop { result: u16, subresult: u16 },
346 #[error(
349 "Invalid public Move function signature. Unsupported return type for return value {index}"
350 )]
351 InvalidPublicFunctionReturnType { index: u16 },
352 #[error("Invalid Transfer Object, object does not have public transfer")]
354 InvalidTransferObject,
355 #[error("Effects of size {current_size} bytes too large. Limit is {max_size} bytes")]
357 EffectsTooLarge {
358 #[cfg_attr(feature = "serde", serde(with = "crate::_serde::ReadableDisplay"))]
359 current_size: u64,
360 #[cfg_attr(feature = "serde", serde(with = "crate::_serde::ReadableDisplay"))]
361 max_size: u64,
362 },
363 #[error(
365 "Publish/Upgrade Error, Missing dependency. A dependency of a published or upgraded package has not been assigned an on-chain address."
366 )]
367 PublishUpgradeMissingDependency,
368 #[error(
374 "Publish/Upgrade Error, Dependency downgrade. Indirect (transitive) dependency of published or upgraded package has been assigned an on-chain version that is less than the version required by one of the package's transitive dependencies."
375 )]
376 PublishUpgradeDependencyDowngrade,
377 #[error("Invalid package upgrade. {kind}")]
379 PackageUpgradeError { kind: PackageUpgradeError },
380 #[error("Written objects of {object_size} bytes too large. Limit is {max_object_size} bytes")]
382 WrittenObjectsTooLarge {
383 #[cfg_attr(feature = "serde", serde(with = "crate::_serde::ReadableDisplay"))]
384 object_size: u64,
385 #[cfg_attr(feature = "serde", serde(with = "crate::_serde::ReadableDisplay"))]
386 max_object_size: u64,
387 },
388 #[error("Certificate is on the deny list")]
390 CertificateDenied,
391 #[error(
393 "IOTA Move Bytecode Verification Timeout. Please run the IOTA Move Verifier for more information."
394 )]
395 IotaMoveVerificationTimeout,
396 #[error("The shared object operation is not allowed")]
398 SharedObjectOperationNotAllowed,
399 #[error("Certificate cannot be executed due to a dependency on a deleted shared object")]
401 InputObjectDeleted,
402 #[error("Certificate is canceled due to congestion on shared objects: {}.", display_congested_objects(.congested_objects))]
404 ExecutionCanceledDueToSharedObjectCongestion { congested_objects: Vec<ObjectId> },
405 #[error("Address {address:?} is denied for coin {coin_type}")]
407 AddressDeniedForCoin { address: Address, coin_type: String },
408 #[error("Coin type is globally paused for use: {coin_type}")]
410 CoinTypeGlobalPause { coin_type: String },
411 #[error("Certificate is canceled because randomness could not be generated this epoch")]
414 ExecutionCanceledDueToRandomnessUnavailable,
415 #[error(
418 "Certificate is canceled due to congestion on shared objects: {}. To give this certificate more priority to be executed, its gas price can be increased to at least {suggested_gas_price}.",
419 display_congested_objects(.congested_objects)
420 )]
421 ExecutionCanceledDueToSharedObjectCongestionV2 {
422 congested_objects: Vec<ObjectId>,
423 #[cfg_attr(feature = "serde", serde(with = "crate::_serde::ReadableDisplay"))]
424 suggested_gas_price: u64,
425 },
426 #[error("A valid linkage was unable to be determined for the transaction")]
429 InvalidLinkage,
430 #[error("Move authentication failed: {error}")]
435 #[cfg_attr(feature = "proptest", weight(0))]
436 MoveAuthentication { error: Box<ExecutionError> },
437 #[error(
441 "Certificate is canceled due to execution-worker congestion. To give this certificate more priority to be executed, its gas price can be increased to at least {suggested_gas_price}."
442 )]
443 ExecutionCanceledDueToExecutionWorkerCongestion {
444 #[cfg_attr(feature = "serde", serde(with = "crate::_serde::ReadableDisplay"))]
445 suggested_gas_price: u64,
446 },
447}
448
449impl ExecutionError {
450 crate::def_is!(
451 InsufficientGas,
452 InvalidGasObject,
453 InvariantViolation,
454 FeatureNotYetSupported,
455 ObjectTooBig,
456 PackageTooBig,
457 CircularObjectOwnership,
458 InsufficientCoinBalance,
459 CoinBalanceOverflow,
460 PublishErrorNonZeroAddress,
461 IotaMoveVerificationError,
462 MovePrimitiveRuntimeError,
463 MoveAbort,
464 VmVerificationOrDeserializationError,
465 VmInvariantViolation,
466 FunctionNotFound,
467 ArityMismatch,
468 TypeArityMismatch,
469 NonEntryFunctionInvoked,
470 CommandArgumentError,
471 TypeArgumentError,
472 UnusedValueWithoutDrop,
473 InvalidPublicFunctionReturnType,
474 InvalidTransferObject,
475 EffectsTooLarge,
476 PublishUpgradeMissingDependency,
477 PublishUpgradeDependencyDowngrade,
478 PackageUpgradeError,
479 WrittenObjectsTooLarge,
480 CertificateDenied,
481 IotaMoveVerificationTimeout,
482 SharedObjectOperationNotAllowed,
483 InputObjectDeleted,
484 ExecutionCanceledDueToSharedObjectCongestion,
485 AddressDeniedForCoin,
486 CoinTypeGlobalPause,
487 ExecutionCanceledDueToRandomnessUnavailable,
488 ExecutionCanceledDueToSharedObjectCongestionV2,
489 InvalidLinkage,
490 MoveAuthentication,
491 ExecutionCanceledDueToExecutionWorkerCongestion,
492 );
493
494 pub fn command_argument_error(kind: CommandArgumentError, argument: u16) -> Self {
495 Self::CommandArgumentError { argument, kind }
496 }
497}
498
499#[derive(Clone, Debug, Eq, Hash, PartialEq)]
509#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
510#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))]
511#[cfg_attr(feature = "bcs-schema", derive(iota_bcs_schema::BcsSchema))]
512pub struct MoveLocation {
513 pub package: ObjectId,
515 pub module: Identifier,
517 pub function: u16,
519 pub instruction: u16,
522 pub function_name: Option<Identifier>,
524}
525
526impl core::fmt::Display for MoveLocation {
527 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> std::fmt::Result {
528 let Self {
529 package,
530 module,
531 function,
532 instruction,
533 function_name,
534 } = self;
535 if let Some(fname) = function_name {
536 write!(
537 f,
538 "{package}::{module}::{fname} (function index {function}) at offset {instruction}"
539 )
540 } else {
541 write!(
542 f,
543 "{package}::{module} in function definition {function} at offset {instruction}"
544 )
545 }
546 }
547}
548
549#[derive(Clone, Debug, Eq, Error, Hash, PartialEq)]
583#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
584#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))]
585#[cfg_attr(feature = "bcs-schema", derive(iota_bcs_schema::BcsSchema))]
586#[non_exhaustive]
587pub enum CommandArgumentError {
588 #[error("The type of the value does not match the expected type")]
590 TypeMismatch,
591 #[error("The argument cannot be deserialized into a value of the specified type")]
593 InvalidBcsBytes,
594 #[error("The argument cannot be instantiated from raw bytes")]
596 InvalidUsageOfPureArgument,
597 #[error(
600 "Invalid argument to private entry function. \
601 These functions cannot take arguments from other Move functions"
602 )]
603 InvalidArgumentToPrivateEntryFunction,
604 #[error("Out of bounds access to input or result vector {index}")]
606 IndexOutOfBounds { index: u16 },
607 #[error(
609 "Out of bounds secondary access to result vector \
610 {result} at secondary index {subresult}"
611 )]
612 SecondaryIndexOutOfBounds { result: u16, subresult: u16 },
613 #[error(
616 "Invalid usage of result {result}, \
617 expected a single result but found either no return values or multiple."
618 )]
619 InvalidResultArity { result: u16 },
620 #[error(
623 "Invalid taking of the Gas coin. \
624 It can only be used by-value with TransferObjects"
625 )]
626 InvalidGasCoinUsage,
627 #[error(
632 "Invalid usage of value. \
633 Mutably borrowed values require unique usage. \
634 Immutably borrowed values cannot be taken or borrowed mutably. \
635 Taken values cannot be used again."
636 )]
637 InvalidValueUsage,
638 #[error("Immutable objects cannot be passed by-value")]
640 InvalidObjectByValue,
641 #[error("Immutable objects cannot be passed by mutable reference, &mut")]
643 InvalidObjectByMutRef,
644 #[error(
647 "Shared object operations such a wrapping, freezing, or converting to owned are not \
648 allowed."
649 )]
650 SharedObjectOperationNotAllowed,
651 #[error(
654 "Invalid argument arity. Expected a single argument but found a result that expanded to \
655 multiple arguments."
656 )]
657 InvalidArgumentArity,
658}
659
660impl CommandArgumentError {
661 crate::def_is!(
662 TypeMismatch,
663 InvalidBcsBytes,
664 InvalidUsageOfPureArgument,
665 InvalidArgumentToPrivateEntryFunction,
666 IndexOutOfBounds,
667 SecondaryIndexOutOfBounds,
668 InvalidResultArity,
669 InvalidGasCoinUsage,
670 InvalidValueUsage,
671 InvalidObjectByValue,
672 InvalidObjectByMutRef,
673 SharedObjectOperationNotAllowed,
674 );
675}
676
677#[derive(Clone, Debug, Eq, Error, Hash, PartialEq)]
699#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
700#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))]
701#[cfg_attr(feature = "bcs-schema", derive(iota_bcs_schema::BcsSchema))]
702#[non_exhaustive]
703pub enum PackageUpgradeError {
704 #[error("Unable to fetch package at {package_id}")]
706 UnableToFetchPackage { package_id: ObjectId },
707 #[error("Object {object_id} is not a package")]
709 NotAPackage { object_id: ObjectId },
710 #[error("New package is incompatible with previous version")]
712 IncompatibleUpgrade,
713 #[error("Digest in upgrade ticket and computed digest disagree")]
715 DigestDoesNotMatch { digest: Digest },
716 #[error("Upgrade policy {policy} is not a valid upgrade policy")]
718 UnknownUpgradePolicy { policy: u8 },
719 #[error("Package ID {package_id} does not match package ID in upgrade ticket {ticket_id}")]
721 PackageIdDoesNotMatch {
722 package_id: ObjectId,
723 ticket_id: ObjectId,
724 },
725}
726
727impl PackageUpgradeError {
728 crate::def_is!(
729 UnableToFetchPackage,
730 NotAPackage,
731 IncompatibleUpgrade,
732 DigestDoesNotMatch,
733 UnknownUpgradePolicy,
734 PackageIdDoesNotMatch,
735 );
736}
737
738#[derive(Clone, Copy, Debug, Eq, Error, Hash, PartialEq)]
750#[cfg_attr(
751 feature = "serde",
752 derive(serde::Deserialize, serde::Serialize),
753 serde(rename_all = "snake_case")
754)]
755#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))]
756#[cfg_attr(feature = "bcs-schema", derive(iota_bcs_schema::BcsSchema))]
757#[non_exhaustive]
758pub enum TypeArgumentError {
759 #[error("A type was not found in the module specified")]
761 TypeNotFound,
762 #[error("A type provided did not match the specified constraints")]
764 ConstraintNotSatisfied,
765}
766
767impl TypeArgumentError {
768 crate::def_is!(TypeNotFound, ConstraintNotSatisfied);
769}
770
771#[cfg(feature = "serde")]
772#[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))]
773mod serialization {
774 use serde::{Deserialize, Deserializer, Serialize, Serializer};
775
776 use super::*;
777
778 #[derive(serde::Deserialize, serde::Serialize)]
779 #[serde(rename = "ExecutionStatus")]
780 struct ReadableExecutionStatus {
781 success: bool,
782 #[serde(skip_serializing_if = "Option::is_none")]
783 status: Option<FailureStatus>,
784 }
785
786 #[derive(serde::Deserialize, serde::Serialize)]
787 struct FailureStatus {
788 error: ExecutionError,
789 #[serde(skip_serializing_if = "Option::is_none")]
790 command: Option<u16>,
791 }
792
793 #[derive(serde::Deserialize, serde::Serialize)]
794 #[serde(rename = "ExecutionStatus")]
795 enum BinaryExecutionStatus {
796 Success,
797 Failure {
798 error: ExecutionError,
799 command: Option<u64>,
800 },
801 }
802
803 impl Serialize for ExecutionStatus {
804 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
805 where
806 S: Serializer,
807 {
808 if serializer.is_human_readable() {
809 let readable = match self.clone() {
810 ExecutionStatus::Success => ReadableExecutionStatus {
811 success: true,
812 status: None,
813 },
814 ExecutionStatus::Failure { error, command } => ReadableExecutionStatus {
815 success: false,
816 status: Some(FailureStatus {
817 error,
818 command: command.map(|c| c as u16),
819 }),
820 },
821 };
822 readable.serialize(serializer)
823 } else {
824 let binary = match self.clone() {
825 ExecutionStatus::Success => BinaryExecutionStatus::Success,
826 ExecutionStatus::Failure { error, command } => {
827 BinaryExecutionStatus::Failure { error, command }
828 }
829 };
830 binary.serialize(serializer)
831 }
832 }
833 }
834
835 impl<'de> Deserialize<'de> for ExecutionStatus {
836 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
837 where
838 D: Deserializer<'de>,
839 {
840 if deserializer.is_human_readable() {
841 let ReadableExecutionStatus { success, status } =
842 Deserialize::deserialize(deserializer)?;
843 match (success, status) {
844 (true, None) => Ok(ExecutionStatus::Success),
845 (false, Some(FailureStatus { error, command })) => {
846 Ok(ExecutionStatus::Failure {
847 error,
848 command: command.map(Into::into),
849 })
850 }
851 (true, Some(_)) | (false, None) => {
853 Err(serde::de::Error::custom("invalid execution status"))
854 }
855 }
856 } else {
857 BinaryExecutionStatus::deserialize(deserializer).map(|readable| match readable {
858 BinaryExecutionStatus::Success => Self::Success,
859 BinaryExecutionStatus::Failure { error, command } => {
860 Self::Failure { error, command }
861 }
862 })
863 }
864 }
865 }
866}