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