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}