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 either doesn't exist and can't be automatically created or it doesn't
68 /// implement the required entrypoint.
69 pub const SYS_INVALID_RECEIVER: ExitCode = ExitCode::new(5);
70 /// The message sender didn't have the requisite funds.
71 pub const SYS_INSUFFICIENT_FUNDS: ExitCode = ExitCode::new(6);
72 /// Message execution (including subcalls) used more gas than the specified limit.
73 pub const SYS_OUT_OF_GAS: ExitCode = ExitCode::new(7);
74 // pub const SYS_RESERVED_8: ExitCode = ExitCode::new(8);
75 /// The message receiver aborted with a reserved exit code.
76 pub const SYS_ILLEGAL_EXIT_CODE: ExitCode = ExitCode::new(9);
77 /// An internal VM assertion failed.
78 pub const SYS_ASSERTION_FAILED: ExitCode = ExitCode::new(10);
79 /// The actor returned a block handle that doesn't exist
80 pub const SYS_MISSING_RETURN: ExitCode = ExitCode::new(11);
81 // pub const SYS_RESERVED_12: ExitCode = ExitCode::new(12);
82 // pub const SYS_RESERVED_13: ExitCode = ExitCode::new(13);
83 // pub const SYS_RESERVED_14: ExitCode = ExitCode::new(14);
84 // pub const SYS_RESERVED_15: ExitCode = ExitCode::new(15);
85
86 /// The lowest exit code that an actor may abort with.
87 pub const FIRST_USER_EXIT_CODE: u32 = 16;
88
89 // Standard exit codes according to the built-in actors' calling convention.
90 /// The method parameters are invalid.
91 pub const USR_ILLEGAL_ARGUMENT: ExitCode = ExitCode::new(16);
92 /// The requested resource does not exist.
93 pub const USR_NOT_FOUND: ExitCode = ExitCode::new(17);
94 /// The requested operation is forbidden.
95 pub const USR_FORBIDDEN: ExitCode = ExitCode::new(18);
96 /// The actor has insufficient funds to perform the requested operation.
97 pub const USR_INSUFFICIENT_FUNDS: ExitCode = ExitCode::new(19);
98 /// The actor's internal state is invalid.
99 pub const USR_ILLEGAL_STATE: ExitCode = ExitCode::new(20);
100 /// There was a de/serialization failure within actor code.
101 pub const USR_SERIALIZATION: ExitCode = ExitCode::new(21);
102 /// The message cannot be handled (usually indicates an unhandled method number).
103 pub const USR_UNHANDLED_MESSAGE: ExitCode = ExitCode::new(22);
104 /// The actor failed with an unspecified error.
105 pub const USR_UNSPECIFIED: ExitCode = ExitCode::new(23);
106 /// The actor failed a user-level assertion.
107 pub const USR_ASSERTION_FAILED: ExitCode = ExitCode::new(24);
108 /// The requested operation cannot be performed in "read-only" mode.
109 pub const USR_READ_ONLY: ExitCode = ExitCode::new(25);
110 /// The method cannot handle a transfer of value.
111 pub const USR_NOT_PAYABLE: ExitCode = ExitCode::new(26);
112 // pub const RESERVED_27: ExitCode = ExitCode::new(27);
113 // pub const RESERVED_28: ExitCode = ExitCode::new(28);
114 // pub const RESERVED_29: ExitCode = ExitCode::new(29);
115 // pub const RESERVED_30: ExitCode = ExitCode::new(30);
116 // pub const RESERVED_31: ExitCode = ExitCode::new(31);
117}
118
119/// When a syscall fails, it returns an `ErrorNumber` to indicate why. The syscalls themselves
120/// include documentation on _which_ syscall errors they can be expected to return, and what they
121/// mean in the context of the syscall.
122#[non_exhaustive]
123#[repr(u32)]
124#[derive(Copy, Clone, Eq, Debug, PartialEq, Error, FromPrimitive)]
125pub enum ErrorNumber {
126 /// A syscall parameters was invalid.
127 IllegalArgument = 1,
128 /// The actor is not in the correct state to perform the requested operation.
129 IllegalOperation = 2,
130 /// This syscall would exceed some system limit (memory, lookback, call depth, etc.).
131 LimitExceeded = 3,
132 /// A system-level assertion has failed.
133 ///
134 /// # Note
135 ///
136 /// Non-system actors should never receive this error number. A system-level assertion will
137 /// cause the entire message to fail.
138 AssertionFailed = 4,
139 /// There were insufficient funds to complete the requested operation.
140 InsufficientFunds = 5,
141 /// A resource was not found.
142 NotFound = 6,
143 /// The specified IPLD block handle was invalid.
144 InvalidHandle = 7,
145 /// The requested CID shape (multihash codec, multihash length) isn't supported.
146 IllegalCid = 8,
147 /// The requested IPLD codec isn't supported.
148 IllegalCodec = 9,
149 /// The IPLD block did not match the specified IPLD codec.
150 Serialization = 10,
151 /// The operation is forbidden.
152 Forbidden = 11,
153 /// The passed buffer is too small.
154 BufferTooSmall = 12,
155 /// The actor is executing in a read-only context.
156 ReadOnly = 13,
157}
158
159impl std::fmt::Display for ErrorNumber {
160 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
161 use ErrorNumber::*;
162 f.write_str(match *self {
163 IllegalArgument => "illegal argument",
164 IllegalOperation => "illegal operation",
165 LimitExceeded => "limit exceeded",
166 AssertionFailed => "filecoin assertion failed",
167 InsufficientFunds => "insufficient funds",
168 NotFound => "resource not found",
169 InvalidHandle => "invalid ipld block handle",
170 IllegalCid => "illegal cid specification",
171 IllegalCodec => "illegal ipld codec",
172 Serialization => "serialization error",
173 Forbidden => "operation forbidden",
174 BufferTooSmall => "buffer too small",
175 ReadOnly => "execution context is read-only",
176 })
177 }
178}