1use crate::ObjectID;
5use crate::base_types::SuiAddress;
6use crate::error::{BoxError, ExecutionError, ExecutionErrorMetadata, ExecutionErrorTrait};
7use move_binary_format::file_format::{CodeOffset, TypeParameterIndex};
8use move_core_types::language_storage::ModuleId;
9use serde::{Deserialize, Serialize};
10use std::error::Error;
11use std::fmt::{self, Display, Formatter};
12use sui_macros::EnumVariantOrder;
13use thiserror::Error;
14
15#[cfg(test)]
16#[path = "unit_tests/execution_status_tests.rs"]
17mod execution_status_tests;
18
19#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize)]
20pub enum ExecutionStatus {
21 Success,
22 Failure(ExecutionFailure),
24}
25
26#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize)]
27pub struct ExecutionFailure {
28 pub error: ExecutionErrorKind,
29 pub command: Option<CommandIndex>,
30}
31
32impl ExecutionFailure {
33 pub fn new(error: ExecutionErrorKind, command: Option<CommandIndex>) -> Self {
34 ExecutionFailure { error, command }
35 }
36}
37
38impl From<ExecutionError> for ExecutionFailure {
39 fn from(value: ExecutionError) -> Self {
40 Self {
41 error: value.kind().clone(),
42 command: value.command(),
43 }
44 }
45}
46
47impl Error for ExecutionFailure {}
48
49impl Display for ExecutionFailure {
50 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
51 write!(f, "Execution Failure: {}", self.error)
52 }
53}
54
55impl ExecutionErrorTrait for ExecutionFailure {
56 fn new(
57 failure: ExecutionFailure,
58 _source: Option<BoxError>,
59 _metadata: ExecutionErrorMetadata,
60 ) -> Self {
61 failure
62 }
63
64 fn with_command_index(self, command: CommandIndex) -> Self {
65 Self {
66 command: Some(command),
67 ..self
68 }
69 }
70 fn kind(&self) -> &ExecutionErrorKind {
71 &self.error
72 }
73 fn command(&self) -> Option<CommandIndex> {
74 self.command
75 }
76}
77
78#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize)]
79pub struct CongestedObjects(pub Vec<ObjectID>);
80
81impl fmt::Display for CongestedObjects {
82 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
83 for obj in &self.0 {
84 write!(f, "{}, ", obj)?;
85 }
86 Ok(())
87 }
88}
89
90pub type ExecutionFailureStatus = ExecutionErrorKind;
91
92#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize, Error, EnumVariantOrder)]
93pub enum ExecutionErrorKind {
94 #[error("Insufficient Gas.")]
98 InsufficientGas,
99 #[error("Invalid Gas Object. Possibly not address-owned or possibly not a SUI coin.")]
100 InvalidGasObject,
101 #[error("INVARIANT VIOLATION.")]
102 InvariantViolation,
103 #[error("Attempted to used feature that is not supported yet")]
104 FeatureNotYetSupported,
105 #[error(
106 "Move object with size {object_size} is larger \
107 than the maximum object size {max_object_size}"
108 )]
109 MoveObjectTooBig {
110 object_size: u64,
111 max_object_size: u64,
112 },
113 #[error(
114 "Move package with size {object_size} is larger than the \
115 maximum object size {max_object_size}"
116 )]
117 MovePackageTooBig {
118 object_size: u64,
119 max_object_size: u64,
120 },
121 #[error("Circular Object Ownership, including object {object}.")]
122 CircularObjectOwnership { object: ObjectID },
123
124 #[error("Insufficient coin balance for operation.")]
128 InsufficientCoinBalance,
129 #[error("The coin balance overflows u64")]
130 CoinBalanceOverflow,
131
132 #[error(
136 "Publish Error, Non-zero Address. \
137 The modules in the package must have their self-addresses set to zero."
138 )]
139 PublishErrorNonZeroAddress,
140
141 #[error(
142 "Sui Move Bytecode Verification Error. \
143 Please run the Sui Move Verifier for more information."
144 )]
145 SuiMoveVerificationError,
146
147 #[error(
152 "Move Primitive Runtime Error. Location: {0}. \
153 Arithmetic error, stack overflow, max value depth, etc."
154 )]
155 MovePrimitiveRuntimeError(MoveLocationOpt),
156 #[error("Move Runtime Abort. Location: {0}, Abort Code: {1}")]
157 MoveAbort(MoveLocation, u64),
158 #[error(
159 "Move Bytecode Verification Error. \
160 Please run the Bytecode Verifier for more information."
161 )]
162 VMVerificationOrDeserializationError,
163 #[error("MOVE VM INVARIANT VIOLATION.")]
164 VMInvariantViolation,
165
166 #[error("Function Not Found.")]
170 FunctionNotFound,
171 #[error(
172 "Arity mismatch for Move function. \
173 The number of arguments does not match the number of parameters"
174 )]
175 ArityMismatch,
176 #[error(
177 "Type arity mismatch for Move function. \
178 Mismatch between the number of actual versus expected type arguments."
179 )]
180 TypeArityMismatch,
181 #[error("Non Entry Function Invoked. Move Call must start with an entry function")]
182 NonEntryFunctionInvoked,
183 #[error("Invalid command argument at {arg_idx}. {kind}")]
184 CommandArgumentError {
185 arg_idx: u16,
186 kind: CommandArgumentError,
187 },
188 #[error("Error for type argument at index {argument_idx}: {kind}")]
189 TypeArgumentError {
190 argument_idx: TypeParameterIndex,
191 kind: TypeArgumentError,
192 },
193 #[error(
194 "Unused result without the drop ability. \
195 Command result {result_idx}, return value {secondary_idx}"
196 )]
197 UnusedValueWithoutDrop { result_idx: u16, secondary_idx: u16 },
198 #[error(
199 "Invalid public Move function signature. \
200 Unsupported return type for return value {idx}"
201 )]
202 InvalidPublicFunctionReturnType { idx: u16 },
203 #[error("Invalid Transfer Object, object does not have public transfer.")]
204 InvalidTransferObject,
205
206 #[error(
211 "Effects of size {current_size} bytes too large. \
212 Limit is {max_size} bytes"
213 )]
214 EffectsTooLarge { current_size: u64, max_size: u64 },
215
216 #[error(
217 "Publish/Upgrade Error, Missing dependency. \
218 A dependency of a published or upgraded package has not been assigned an on-chain \
219 address."
220 )]
221 PublishUpgradeMissingDependency,
222
223 #[error(
224 "Publish/Upgrade Error, Dependency downgrade. \
225 Indirect (transitive) dependency of published or upgraded package has been assigned an \
226 on-chain version that is less than the version required by one of the package's \
227 transitive dependencies."
228 )]
229 PublishUpgradeDependencyDowngrade,
230
231 #[error("Invalid package upgrade. {upgrade_error}")]
232 PackageUpgradeError { upgrade_error: PackageUpgradeError },
233
234 #[error(
236 "Written objects of {current_size} bytes too large. \
237 Limit is {max_size} bytes"
238 )]
239 WrittenObjectsTooLarge { current_size: u64, max_size: u64 },
240
241 #[error("Certificate is on the deny list")]
242 CertificateDenied,
243
244 #[error(
245 "Sui Move Bytecode Verification Timeout. \
246 Please run the Sui Move Verifier for more information."
247 )]
248 SuiMoveVerificationTimedout,
249
250 #[error("The shared object operation is not allowed.")]
251 SharedObjectOperationNotAllowed,
252
253 #[error(
254 "Certificate cannot be executed due to a dependency on a deleted shared object or an object that was transferred out of consensus"
255 )]
256 InputObjectDeleted,
257
258 #[error("Certificate is cancelled due to congestion on shared objects: {congested_objects}")]
259 ExecutionCancelledDueToSharedObjectCongestion { congested_objects: CongestedObjects },
260
261 #[error("Address {address:?} is denied for coin {coin_type}")]
262 AddressDeniedForCoin {
263 address: SuiAddress,
264 coin_type: String,
265 },
266
267 #[error("Coin type is globally paused for use: {coin_type}")]
268 CoinTypeGlobalPause { coin_type: String },
269
270 #[error("Certificate is cancelled because randomness could not be generated this epoch")]
271 ExecutionCancelledDueToRandomnessUnavailable,
272
273 #[error(
274 "Move vector element (passed to MakeMoveVec) with size {value_size} is larger \
275 than the maximum size {max_scaled_size}. Note that this maximum is scaled based on the \
276 type of the vector element."
277 )]
278 MoveVectorElemTooBig {
279 value_size: u64,
280 max_scaled_size: u64,
281 },
282
283 #[error(
284 "Move value (possibly an upgrade ticket or a dev-inspect value) with size {value_size} \
285 is larger than the maximum size {max_scaled_size}. Note that this maximum is scaled based \
286 on the type of the value."
287 )]
288 MoveRawValueTooBig {
289 value_size: u64,
290 max_scaled_size: u64,
291 },
292
293 #[error("A valid linkage was unable to be determined for the transaction")]
294 InvalidLinkage,
295
296 #[error("Insufficient funds for funds accumulator withdrawal")]
297 InsufficientFundsForWithdraw,
298
299 #[error("Non-exclusive write input object {id} has been modified")]
300 NonExclusiveWriteInputObjectModified { id: ObjectID },
301
302 #[error("System object not available locally; transaction must be retried")]
307 SystemObjectNotAvailableLocally,
308 }
311
312#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize, Hash)]
313pub struct MoveLocation {
314 pub module: ModuleId,
315 pub function: u16,
316 pub instruction: CodeOffset,
317 pub function_name: Option<String>,
318}
319
320#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize, Hash)]
321pub struct MoveLocationOpt(pub Option<MoveLocation>);
322
323#[derive(Eq, PartialEq, Copy, Clone, Debug, Serialize, Deserialize, Hash, Error)]
324pub enum CommandArgumentError {
325 #[error("The type of the value does not match the expected type")]
326 TypeMismatch,
327 #[error("The argument cannot be deserialized into a value of the specified type")]
328 InvalidBCSBytes,
329 #[error("The argument cannot be instantiated from raw bytes")]
330 InvalidUsageOfPureArg,
331 #[error(
332 "Invalid argument to private entry function. \
333 These functions cannot take arguments from other Move functions"
334 )]
335 InvalidArgumentToPrivateEntryFunction,
336 #[error("Out of bounds access to input or result vector {idx}")]
337 IndexOutOfBounds { idx: u16 },
338 #[error(
339 "Out of bounds secondary access to result vector \
340 {result_idx} at secondary index {secondary_idx}"
341 )]
342 SecondaryIndexOutOfBounds { result_idx: u16, secondary_idx: u16 },
343 #[error(
344 "Invalid usage of result {result_idx}, \
345 expected a single result but found either no return values or multiple."
346 )]
347 InvalidResultArity { result_idx: u16 },
348 #[error(
349 "Invalid taking of the Gas coin. \
350 It can only be used by-value with TransferObjects"
351 )]
352 InvalidGasCoinUsage,
353 #[error(
354 "Invalid usage of value. \
355 Mutably borrowed values require unique usage. \
356 Immutably borrowed values cannot be taken or borrowed mutably. \
357 Taken values cannot be used again."
358 )]
359 InvalidValueUsage,
360 #[error("Immutable objects cannot be passed by-value.")]
361 InvalidObjectByValue,
362 #[error("Immutable objects cannot be passed by mutable reference, &mut.")]
363 InvalidObjectByMutRef,
364 #[error(
365 "Shared object operations such a wrapping, freezing, or converting to owned are not \
366 allowed."
367 )]
368 SharedObjectOperationNotAllowed,
369 #[error(
370 "Invalid argument arity. Expected a single argument but found a result that expanded to \
371 multiple arguments."
372 )]
373 InvalidArgumentArity,
374 #[error(
375 "Object passed to TransferObject does not have public transfer, i.e. the `store` \
376 ability"
377 )]
378 InvalidTransferObject,
379 #[error(
380 "First argument to MakeMoveVec is not an object. If no type is specified for MakeMoveVec, all arguments must be the same object type."
381 )]
382 InvalidMakeMoveVecNonObjectArgument,
383 #[error("Specified argument location does not have a value and cannot be used")]
384 ArgumentWithoutValue,
385 #[error(
386 "Cannot move a borrowed value. The value's type does resulted in this argument usage \
387 being inferred as a move. This is likely due to the type not having the `copy` ability; \
388 although in rare cases, it could also be this is the last usage of a value without the \
389 `drop` ability."
390 )]
391 CannotMoveBorrowedValue,
392 #[error(
393 "Cannot write to an argument location that is still borrowed, and where that borrow \
394 is an extension of that reference. This is likely due to this argument being used in a \
395 Move call that returns a reference, and that reference is used in a later command."
396 )]
397 CannotWriteToExtendedReference,
398 #[error(
399 "The argument specified cannot be used as a reference argument in the Move call. Either \
400 the argument is a mutable reference and it conflicts with another argument to the call, \
401 or the argument is mutable and another reference extends it and will be used in a later \
402 command."
403 )]
404 InvalidReferenceArgument,
405}
406
407#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize, Hash, Error)]
408pub enum PackageUpgradeError {
409 #[error("Unable to fetch package at {package_id}")]
410 UnableToFetchPackage { package_id: ObjectID },
411 #[error("Object {object_id} is not a package")]
412 NotAPackage { object_id: ObjectID },
413 #[error("New package is incompatible with previous version")]
414 IncompatibleUpgrade,
415 #[error("Digest in upgrade ticket and computed digest disagree")]
416 DigestDoesNotMatch { digest: Vec<u8> },
417 #[error("Upgrade policy {policy} is not a valid upgrade policy")]
418 UnknownUpgradePolicy { policy: u8 },
419 #[error("Package ID {package_id} does not match package ID in upgrade ticket {ticket_id}")]
420 PackageIDDoesNotMatch {
421 package_id: ObjectID,
422 ticket_id: ObjectID,
423 },
424}
425
426#[derive(Eq, PartialEq, Clone, Copy, Debug, Serialize, Deserialize, Hash, Error)]
427pub enum TypeArgumentError {
428 #[error("A type was not found in the module specified.")]
429 TypeNotFound,
430 #[error("A type provided did not match the specified constraints.")]
431 ConstraintNotSatisfied,
432}
433
434impl ExecutionErrorKind {
435 pub fn command_argument_error(kind: CommandArgumentError, arg_idx: u16) -> Self {
436 Self::CommandArgumentError { arg_idx, kind }
437 }
438}
439
440impl Display for MoveLocationOpt {
441 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
442 match &self.0 {
443 None => write!(f, "UNKNOWN"),
444 Some(l) => write!(f, "{l}"),
445 }
446 }
447}
448
449impl Display for MoveLocation {
450 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
451 let Self {
452 module,
453 function,
454 instruction,
455 function_name,
456 } = self;
457 if let Some(fname) = function_name {
458 write!(
459 f,
460 "{module}::{fname} (function index {function}) at offset {instruction}"
461 )
462 } else {
463 write!(
464 f,
465 "{module} in function definition {function} at offset {instruction}"
466 )
467 }
468 }
469}
470
471impl ExecutionStatus {
472 pub fn new_failure(failure: ExecutionFailure) -> ExecutionStatus {
473 ExecutionStatus::Failure(failure)
474 }
475
476 pub fn is_ok(&self) -> bool {
477 matches!(self, ExecutionStatus::Success)
478 }
479
480 pub fn is_err(&self) -> bool {
481 matches!(self, ExecutionStatus::Failure(_))
482 }
483
484 pub fn unwrap(&self) {
485 match self {
486 ExecutionStatus::Success => {}
487 ExecutionStatus::Failure(_) => {
488 panic!("Unable to unwrap() on {:?}", self);
489 }
490 }
491 }
492
493 pub fn unwrap_err(self) -> (ExecutionErrorKind, Option<CommandIndex>) {
494 match self {
495 ExecutionStatus::Success => {
496 panic!("Unable to unwrap() on {:?}", self);
497 }
498 ExecutionStatus::Failure(ExecutionFailure { error, command }) => (error, command),
499 }
500 }
501
502 pub fn get_congested_objects(&self) -> Option<&CongestedObjects> {
503 if let ExecutionStatus::Failure(ExecutionFailure {
504 error:
505 ExecutionErrorKind::ExecutionCancelledDueToSharedObjectCongestion { congested_objects },
506 ..
507 }) = self
508 {
509 Some(congested_objects)
510 } else {
511 None
512 }
513 }
514
515 pub fn is_cancelled(&self) -> bool {
516 matches!(
517 self,
518 ExecutionStatus::Failure(ExecutionFailure {
519 error: ExecutionErrorKind::ExecutionCancelledDueToSharedObjectCongestion { .. },
520 ..
521 })
522 )
523 }
524}
525
526pub type CommandIndex = usize;