fvm_shared/error/mod.rs
1// Copyright 2021-2023 Protocol Labs
2// SPDX-License-Identifier: Apache-2.0, MIT
3use std::fmt::Formatter;
4
5use num_derive::FromPrimitive;
6use serde::{Deserialize, Serialize};
7use thiserror::Error;
8
9/// ExitCode defines the exit code from the VM invocation.
10#[derive(PartialEq, Eq, Debug, Clone, Copy, Serialize, Deserialize)]
11#[serde(transparent)]
12#[repr(transparent)]
13pub struct ExitCode {
14 value: u32,
15}
16
17impl ExitCode {
18 pub const fn new(value: u32) -> Self {
19 Self { value }
20 }
21
22 pub fn value(self) -> u32 {
23 self.value
24 }
25
26 /// Returns true if the exit code indicates success.
27 pub fn is_success(self) -> bool {
28 self.value == 0
29 }
30
31 /// Returns true if the error code is in the range of exit codes reserved for the VM
32 /// (including Ok).
33 pub fn is_system_error(self) -> bool {
34 self.value < (Self::FIRST_USER_EXIT_CODE)
35 }
36}
37
38impl From<u32> for ExitCode {
39 fn from(value: u32) -> Self {
40 ExitCode { value }
41 }
42}
43
44impl std::fmt::Display for ExitCode {
45 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
46 write!(f, "{}", self.value)
47 }
48}
49
50impl ExitCode {
51 // Exit codes which originate inside the VM.
52 // These values may not be used by actors when aborting.
53
54 /// The code indicating successful execution.
55 pub const OK: ExitCode = ExitCode::new(0);
56 /// The message sender doesn't exist.
57 pub const SYS_SENDER_INVALID: ExitCode = ExitCode::new(1);
58 /// The message sender was not in a valid state to send this message.
59 ///
60 /// Either:
61 /// - The sender's nonce nonce didn't match the message nonce.
62 /// - The sender didn't have the funds to cover the message gas.
63 pub const SYS_SENDER_STATE_INVALID: ExitCode = ExitCode::new(2);
64 //pub const SYS_RESERVED_3 ExitCode = ExitCode::new(3);
65 /// The message receiver trapped (panicked).
66 pub const SYS_ILLEGAL_INSTRUCTION: ExitCode = ExitCode::new(4);
67 /// The message receiver doesn't exist and can't be automatically created
68 pub const SYS_INVALID_RECEIVER: ExitCode = ExitCode::new(5);
69 /// The message sender didn't have the requisite funds.
70 pub const SYS_INSUFFICIENT_FUNDS: ExitCode = ExitCode::new(6);
71 /// Message execution (including subcalls) used more gas than the specified limit.
72 pub const SYS_OUT_OF_GAS: ExitCode = ExitCode::new(7);
73 // pub const SYS_RESERVED_8: ExitCode = ExitCode::new(8);
74 /// The message receiver aborted with a reserved exit code.
75 pub const SYS_ILLEGAL_EXIT_CODE: ExitCode = ExitCode::new(9);
76 /// An internal VM assertion failed.
77 pub const SYS_ASSERTION_FAILED: ExitCode = ExitCode::new(10);
78 /// The actor returned a block handle that doesn't exist
79 pub const SYS_MISSING_RETURN: ExitCode = ExitCode::new(11);
80 // pub const SYS_RESERVED_12: ExitCode = ExitCode::new(12);
81 // pub const SYS_RESERVED_13: ExitCode = ExitCode::new(13);
82 // pub const SYS_RESERVED_14: ExitCode = ExitCode::new(14);
83 // pub const SYS_RESERVED_15: ExitCode = ExitCode::new(15);
84
85 /// The lowest exit code that an actor may abort with.
86 pub const FIRST_USER_EXIT_CODE: u32 = 16;
87
88 // Standard exit codes according to the built-in actors' calling convention.
89 /// The method parameters are invalid.
90 pub const USR_ILLEGAL_ARGUMENT: ExitCode = ExitCode::new(16);
91 /// The requested resource does not exist.
92 pub const USR_NOT_FOUND: ExitCode = ExitCode::new(17);
93 /// The requested operation is forbidden.
94 pub const USR_FORBIDDEN: ExitCode = ExitCode::new(18);
95 /// The actor has insufficient funds to perform the requested operation.
96 pub const USR_INSUFFICIENT_FUNDS: ExitCode = ExitCode::new(19);
97 /// The actor's internal state is invalid.
98 pub const USR_ILLEGAL_STATE: ExitCode = ExitCode::new(20);
99 /// There was a de/serialization failure within actor code.
100 pub const USR_SERIALIZATION: ExitCode = ExitCode::new(21);
101 /// The message cannot be handled (usually indicates an unhandled method number).
102 pub const USR_UNHANDLED_MESSAGE: ExitCode = ExitCode::new(22);
103 /// The actor failed with an unspecified error.
104 pub const USR_UNSPECIFIED: ExitCode = ExitCode::new(23);
105 /// The actor failed a user-level assertion.
106 pub const USR_ASSERTION_FAILED: ExitCode = ExitCode::new(24);
107 /// The requested operation cannot be performed in "read-only" mode.
108 pub const USR_READ_ONLY: ExitCode = ExitCode::new(25);
109 /// The method cannot handle a transfer of value.
110 pub const USR_NOT_PAYABLE: ExitCode = ExitCode::new(26);
111 // pub const RESERVED_27: ExitCode = ExitCode::new(27);
112 // pub const RESERVED_28: ExitCode = ExitCode::new(28);
113 // pub const RESERVED_29: ExitCode = ExitCode::new(29);
114 // pub const RESERVED_30: ExitCode = ExitCode::new(30);
115 // pub const RESERVED_31: ExitCode = ExitCode::new(31);
116}
117
118/// When a syscall fails, it returns an `ErrorNumber` to indicate why. The syscalls themselves
119/// include documentation on _which_ syscall errors they can be expected to return, and what they
120/// mean in the context of the syscall.
121#[non_exhaustive]
122#[repr(u32)]
123#[derive(Copy, Clone, Eq, Debug, PartialEq, Error, FromPrimitive)]
124pub enum ErrorNumber {
125 /// A syscall parameters was invalid.
126 IllegalArgument = 1,
127 /// The actor is not in the correct state to perform the requested operation.
128 IllegalOperation = 2,
129 /// This syscall would exceed some system limit (memory, lookback, call depth, etc.).
130 LimitExceeded = 3,
131 /// A system-level assertion has failed.
132 ///
133 /// # Note
134 ///
135 /// Non-system actors should never receive this error number. A system-level assertion will
136 /// cause the entire message to fail.
137 AssertionFailed = 4,
138 /// There were insufficient funds to complete the requested operation.
139 InsufficientFunds = 5,
140 /// A resource was not found.
141 NotFound = 6,
142 /// The specified IPLD block handle was invalid.
143 InvalidHandle = 7,
144 /// The requested CID shape (multihash codec, multihash length) isn't supported.
145 IllegalCid = 8,
146 /// The requested IPLD codec isn't supported.
147 IllegalCodec = 9,
148 /// The IPLD block did not match the specified IPLD codec.
149 Serialization = 10,
150 /// The operation is forbidden.
151 Forbidden = 11,
152 /// The passed buffer is too small.
153 BufferTooSmall = 12,
154 /// The actor is executing in a read-only context.
155 ReadOnly = 13,
156}
157
158impl std::fmt::Display for ErrorNumber {
159 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
160 use ErrorNumber::*;
161 f.write_str(match *self {
162 IllegalArgument => "illegal argument",
163 IllegalOperation => "illegal operation",
164 LimitExceeded => "limit exceeded",
165 AssertionFailed => "filecoin assertion failed",
166 InsufficientFunds => "insufficient funds",
167 NotFound => "resource not found",
168 InvalidHandle => "invalid ipld block handle",
169 IllegalCid => "illegal cid specification",
170 IllegalCodec => "illegal ipld codec",
171 Serialization => "serialization error",
172 Forbidden => "operation forbidden",
173 BufferTooSmall => "buffer too small",
174 ReadOnly => "execution context is read-only",
175 })
176 }
177}