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}