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