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