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