odra_core/
error.rs

1use casper_types::bytesrepr::Error as BytesReprError;
2use casper_types::{CLType, CLValueError};
3use core::any::Any;
4
5use crate::arithmetic::ArithmeticsError;
6use crate::prelude::*;
7use crate::VmError::Serialization;
8
9/// The name of the generic Casper error used in Odra framework.
10/// When we get an error from the Casper VM, we use this name to represent it,
11/// because the Casper VM does not provide a specific error name, but the code
12/// only.
13pub const CASPER_ERROR_GENERIC_NAME: &str = "CasperExecError";
14
15/// General error type in Odra framework
16#[repr(u16)]
17#[derive(Clone, Debug, PartialEq)]
18pub enum OdraError {
19    /// An error that can occur during smart contract execution
20    ExecutionError(ExecutionError),
21    /// An internal virtual machine error
22    VmError(VmError)
23}
24
25impl OdraError {
26    /// Returns the error code.
27    pub fn code(&self) -> u16 {
28        match self {
29            OdraError::ExecutionError(e) => e.code(),
30            OdraError::VmError(_e) => 0
31        }
32    }
33
34    #[cfg(target_arch = "wasm32")]
35    /// Creates a new user error with a given code.
36    pub fn user(code: u16) -> Self {
37        if code >= ExecutionError::UserErrorTooHigh.code() {
38            ExecutionError::UserErrorTooHigh.into()
39        } else {
40            ExecutionError::User(code).into()
41        }
42    }
43
44    #[cfg(not(target_arch = "wasm32"))]
45    /// Creates a new user error with a given code.
46    pub fn user(code: u16, msg: &str) -> Self {
47        if code >= ExecutionError::UserErrorTooHigh.code() {
48            ExecutionError::UserErrorTooHigh.into()
49        } else {
50            ExecutionError::User(UserError {
51                code,
52                message: msg.to_string()
53            })
54            .into()
55        }
56    }
57}
58
59impl From<ArithmeticsError> for ExecutionError {
60    fn from(error: ArithmeticsError) -> Self {
61        match error {
62            ArithmeticsError::AdditionOverflow => Self::AdditionOverflow,
63            ArithmeticsError::SubtractingOverflow => Self::SubtractionOverflow,
64            ArithmeticsError::ConversionError => Self::ConversionError
65        }
66    }
67}
68
69impl From<ArithmeticsError> for OdraError {
70    fn from(error: ArithmeticsError) -> Self {
71        Into::<ExecutionError>::into(error).into()
72    }
73}
74
75impl From<Box<dyn Any + Send>> for OdraError {
76    fn from(_: Box<dyn Any + Send>) -> Self {
77        OdraError::VmError(VmError::Panic)
78    }
79}
80
81impl From<casper_types::bytesrepr::Error> for ExecutionError {
82    fn from(error: casper_types::bytesrepr::Error) -> Self {
83        match error {
84            casper_types::bytesrepr::Error::EarlyEndOfStream => Self::EarlyEndOfStream,
85            casper_types::bytesrepr::Error::Formatting => Self::Formatting,
86            casper_types::bytesrepr::Error::LeftOverBytes => Self::LeftOverBytes,
87            casper_types::bytesrepr::Error::OutOfMemory => Self::OutOfMemory,
88            casper_types::bytesrepr::Error::NotRepresentable => Self::NotRepresentable,
89            casper_types::bytesrepr::Error::ExceededRecursionDepth => Self::ExceededRecursionDepth,
90            _ => Self::Formatting
91        }
92    }
93}
94
95/// An error that can occur during smart contract execution
96///
97/// It is represented by an error code and a human-readable message.
98///
99/// Errors codes 0..32767 are available for the user to define custom error
100/// in smart contracts.
101/// 32768 code is a special code representing a violation of the custom error code space.
102///
103/// The rest of codes 32769..[u16::MAX](u16::MAX), are used internally by the framework.
104#[repr(u16)]
105#[derive(Clone, Debug, PartialEq)]
106pub enum ExecutionError {
107    /// Unwrap error.
108    UnwrapError = 1,
109    /// Something unexpected happened.
110    UnexpectedError = 2,
111    /// Addition overflow
112    AdditionOverflow = 100,
113    /// Subtraction overflow
114    SubtractionOverflow = 101,
115    /// Method does not accept deposit
116    NonPayable = 102,
117    /// Can't transfer tokens to contract.
118    TransferToContract = 103,
119    /// Reentrant call detected
120    ReentrantCall = 104,
121    /// Contract already installed
122    ContractAlreadyInstalled = 105,
123    /// Unknown constructor
124    UnknownConstructor = 106,
125    /// Native transfer error
126    NativeTransferError = 107,
127    /// Index out of bounds
128    IndexOutOfBounds = 108,
129    /// Tried to construct a zero address.
130    ZeroAddress = 109,
131    /// Address creation failed
132    AddressCreationFailed = 110,
133    /// Early end of stream - deserialization error
134    EarlyEndOfStream = 111,
135    /// Formatting error - deserialization error
136    Formatting = 112,
137    /// Left over bytes - deserialization error
138    LeftOverBytes = 113,
139    /// Out of memory
140    OutOfMemory = 114,
141    /// Not representable
142    NotRepresentable = 115,
143    /// Exceeded recursion depth
144    ExceededRecursionDepth = 116,
145    /// Key not found
146    KeyNotFound = 117,
147    /// Could not deserialize signature
148    CouldNotDeserializeSignature = 118,
149    /// Type mismatch
150    TypeMismatch = 119,
151    /// Could not sign message
152    CouldNotSignMessage = 120,
153    /// Empty dictionary name
154    EmptyDictionaryName = 121,
155    /// Calling a contract with missing entrypoint arguments.
156    MissingArg = 122,
157    /// Reading the address from the storage failed.
158    MissingAddress = 123,
159    /// Out of gas error
160    OutOfGas = 124,
161    /// MainPurse error
162    MainPurseError = 125,
163    /// Conversion error
164    ConversionError = 126,
165    /// Couldn't deploy the contract
166    ContractDeploymentError = 127,
167    /// Couldn't extract caller info
168    CannotExtractCallerInfo = 128,
169    /// Maximum code for user errors
170    MaxUserError = 64535,
171    /// User error too high. The code should be in range 0..32767.
172    UserErrorTooHigh = 64536,
173    /// User error
174    #[cfg(target_arch = "wasm32")]
175    User(u16),
176    #[cfg(not(target_arch = "wasm32"))]
177    /// User error
178    User(UserError)
179}
180
181#[cfg(not(target_arch = "wasm32"))]
182#[derive(Debug, Clone)]
183pub struct UserError {
184    code: u16,
185    message: String
186}
187
188#[cfg(not(target_arch = "wasm32"))]
189impl PartialEq for UserError {
190    fn eq(&self, other: &Self) -> bool {
191        if self.message == CASPER_ERROR_GENERIC_NAME || other.message == CASPER_ERROR_GENERIC_NAME {
192            return self.code == other.code;
193        }
194        // Compare both code and message for equality
195        self.code == other.code && self.message == other.message
196    }
197}
198
199impl ExecutionError {
200    /// Returns the error code.
201    pub fn code(&self) -> u16 {
202        unsafe {
203            match self {
204                #[cfg(target_arch = "wasm32")]
205                ExecutionError::User(code) => *code,
206                #[cfg(not(target_arch = "wasm32"))]
207                ExecutionError::User(UserError { code, .. }) => *code,
208                ExecutionError::MaxUserError => 64535,
209                ExecutionError::UserErrorTooHigh => 64536,
210                _ => ExecutionError::UserErrorTooHigh.code() + *(self as *const Self as *const u16)
211            }
212        }
213    }
214}
215
216impl From<ExecutionError> for OdraError {
217    fn from(error: ExecutionError) -> Self {
218        Self::ExecutionError(error)
219    }
220}
221
222/// An internal virtual machine error
223#[derive(Clone, Debug, PartialEq, Eq)]
224pub enum VmError {
225    /// Failed to serialize a value.
226    Serialization,
227    /// Failed to deserialize a value.
228    Deserialization,
229    /// Exceeded the account balance
230    BalanceExceeded,
231    /// Non existing host entrypoint was called.
232    NoSuchMethod(String),
233    /// Accessing a contract with an invalid address.
234    InvalidContractAddress,
235    /// Error calling a host function in a wrong context.
236    InvalidContext,
237    /// Calling a contract with a wrong argument type.
238    TypeMismatch {
239        /// Expected type.
240        expected: CLType,
241        /// Found type.
242        found: CLType
243    },
244    /// Non-specified error with a custom message.
245    Other(String),
246    /// Unspecified error.
247    Panic
248}
249
250/// Error that can occur while operating on a collection.
251pub enum CollectionError {
252    /// The requested index is bigger than the max collection index.
253    IndexOutOfBounds
254}
255
256impl From<CollectionError> for ExecutionError {
257    fn from(error: CollectionError) -> Self {
258        match error {
259            CollectionError::IndexOutOfBounds => Self::IndexOutOfBounds
260        }
261    }
262}
263
264impl From<CollectionError> for OdraError {
265    fn from(error: CollectionError) -> Self {
266        Into::<ExecutionError>::into(error).into()
267    }
268}
269
270/// Error that can occur while operating on an Address.
271#[derive(Clone, Debug, PartialEq)]
272pub enum AddressError {
273    /// Tried to construct a zero address.
274    ZeroAddress,
275    /// Tried to construct an address and failed.
276    AddressCreationError
277}
278
279impl From<AddressError> for ExecutionError {
280    fn from(error: AddressError) -> Self {
281        match error {
282            AddressError::ZeroAddress => Self::ZeroAddress,
283            AddressError::AddressCreationError => Self::AddressCreationFailed
284        }
285    }
286}
287
288impl From<AddressError> for OdraError {
289    fn from(error: AddressError) -> Self {
290        Into::<ExecutionError>::into(error).into()
291    }
292}
293
294/// Event-related errors.
295#[derive(Debug, PartialEq, Eq, PartialOrd)]
296pub enum EventError {
297    /// The type of event is different from expected.
298    UnexpectedType(String),
299    /// Index of the event is out of bounds.
300    IndexOutOfBounds,
301    /// Formatting error while deserializing.
302    Formatting,
303    /// Unexpected error while deserializing.
304    Parsing,
305    /// Could not extract event name.
306    CouldntExtractName,
307    /// Could not extract event data.
308    CouldntExtractEventData,
309    /// Contract doesn't support CES events.
310    ContractDoesntSupportEvents,
311    /// Tried to query event for a non-contract entity.
312    TriedToQueryEventForNonContract
313}
314
315/// Represents the result of a contract call.
316pub type OdraResult<T> = Result<T, OdraError>;
317
318impl From<CLValueError> for OdraError {
319    fn from(error: CLValueError) -> Self {
320        match error {
321            CLValueError::Serialization(_) => OdraError::VmError(Serialization),
322            CLValueError::Type(cl_type_mismatch) => OdraError::VmError(VmError::TypeMismatch {
323                expected: cl_type_mismatch.expected.clone(),
324                found: cl_type_mismatch.found.clone()
325            })
326        }
327    }
328}
329
330impl From<BytesReprError> for OdraError {
331    fn from(error: BytesReprError) -> Self {
332        match error {
333            BytesReprError::EarlyEndOfStream => ExecutionError::EarlyEndOfStream,
334            BytesReprError::Formatting => ExecutionError::Formatting,
335            BytesReprError::LeftOverBytes => ExecutionError::LeftOverBytes,
336            BytesReprError::OutOfMemory => ExecutionError::OutOfMemory,
337            BytesReprError::NotRepresentable => ExecutionError::NotRepresentable,
338            BytesReprError::ExceededRecursionDepth => ExecutionError::ExceededRecursionDepth,
339            _ => ExecutionError::Formatting
340        }
341        .into()
342    }
343}
344
345impl From<anyhow::Error> for OdraError {
346    fn from(value: anyhow::Error) -> Self {
347        OdraError::VmError(VmError::Other(value.to_string()))
348    }
349}