Skip to main content

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    CannotOverrideKeys = 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    /// Upgrading a contract that is not installed.
170    ContractNotInstalled = 129,
171    /// Upgrading a contract without previous version
172    UpgradingWithoutPreviousVersion = 130,
173    /// Upgrading not a contract
174    UpgradingNotAContract = 131,
175    /// Upgrading a contract with a schema that does not match the previous version.
176    SchemaMismatch = 132,
177    /// Cannot disable a previous version of a contract.
178    CannotDisablePreviousVersion = 133,
179    /// Cannot upgrade a contract without an upgrade function.
180    CannotUpgradeWithoutUpgrade = 134,
181    /// Factory module function should not be called directly.
182    FactoryModuleCall = 135,
183    /// Cannot get an immediate caller
184    CannotGetAnImmediateCaller = 136,
185    /// Path index out of bounds.
186    PathIndexOutOfBounds = 137,
187    /// Maximum code for user errors
188    MaxUserError = 64535,
189    /// User error too high. The code should be in range 0..32767.
190    UserErrorTooHigh = 64536,
191    /// User error
192    #[cfg(target_arch = "wasm32")]
193    User(u16),
194    #[cfg(not(target_arch = "wasm32"))]
195    /// User error
196    User(UserError)
197}
198
199#[cfg(not(target_arch = "wasm32"))]
200#[derive(Debug, Clone)]
201pub struct UserError {
202    code: u16,
203    message: String
204}
205
206#[cfg(not(target_arch = "wasm32"))]
207impl PartialEq for UserError {
208    fn eq(&self, other: &Self) -> bool {
209        if self.message == CASPER_ERROR_GENERIC_NAME || other.message == CASPER_ERROR_GENERIC_NAME {
210            return self.code == other.code;
211        }
212        // Compare both code and message for equality
213        self.code == other.code && self.message == other.message
214    }
215}
216
217impl ExecutionError {
218    /// Returns the error code.
219    pub fn code(&self) -> u16 {
220        unsafe {
221            match self {
222                #[cfg(target_arch = "wasm32")]
223                ExecutionError::User(code) => *code,
224                #[cfg(not(target_arch = "wasm32"))]
225                ExecutionError::User(UserError { code, .. }) => *code,
226                ExecutionError::MaxUserError => 64535,
227                ExecutionError::UserErrorTooHigh => 64536,
228                _ => ExecutionError::UserErrorTooHigh.code() + *(self as *const Self as *const u16)
229            }
230        }
231    }
232}
233
234impl From<ExecutionError> for OdraError {
235    fn from(error: ExecutionError) -> Self {
236        Self::ExecutionError(error)
237    }
238}
239
240/// An internal virtual machine error
241#[derive(Clone, Debug, PartialEq, Eq)]
242pub enum VmError {
243    /// Failed to serialize a value.
244    Serialization,
245    /// Failed to deserialize a value.
246    Deserialization,
247    /// Exceeded the account balance
248    BalanceExceeded,
249    /// Non existing host entrypoint was called.
250    NoSuchMethod(String),
251    /// Accessing a contract with an invalid address.
252    InvalidContractAddress,
253    /// Error calling a host function in a wrong context.
254    InvalidContext,
255    /// Calling a contract with a wrong argument type.
256    TypeMismatch {
257        /// Expected type.
258        expected: CLType,
259        /// Found type.
260        found: CLType
261    },
262    /// Non-specified error with a custom message.
263    Other(String),
264    /// Unspecified error.
265    Panic
266}
267
268/// Error that can occur while operating on a collection.
269pub enum CollectionError {
270    /// The requested index is bigger than the max collection index.
271    IndexOutOfBounds
272}
273
274impl From<CollectionError> for ExecutionError {
275    fn from(error: CollectionError) -> Self {
276        match error {
277            CollectionError::IndexOutOfBounds => Self::IndexOutOfBounds
278        }
279    }
280}
281
282impl From<CollectionError> for OdraError {
283    fn from(error: CollectionError) -> Self {
284        Into::<ExecutionError>::into(error).into()
285    }
286}
287
288/// Error that can occur while operating on an Address.
289#[derive(Clone, Debug, PartialEq)]
290pub enum AddressError {
291    /// Tried to construct a zero address.
292    ZeroAddress,
293    /// Tried to construct an address and failed.
294    AddressCreationError
295}
296
297impl From<AddressError> for ExecutionError {
298    fn from(error: AddressError) -> Self {
299        match error {
300            AddressError::ZeroAddress => Self::ZeroAddress,
301            AddressError::AddressCreationError => Self::AddressCreationFailed
302        }
303    }
304}
305
306impl From<AddressError> for OdraError {
307    fn from(error: AddressError) -> Self {
308        Into::<ExecutionError>::into(error).into()
309    }
310}
311
312/// Event-related errors.
313#[derive(Debug, PartialEq, Eq, PartialOrd)]
314pub enum EventError {
315    /// The type of event is different from expected.
316    UnexpectedType(String),
317    /// Index of the event is out of bounds.
318    IndexOutOfBounds,
319    /// Formatting error while deserializing.
320    Formatting,
321    /// Unexpected error while deserializing.
322    Parsing,
323    /// Could not extract event name.
324    CouldntExtractName,
325    /// Could not extract event data.
326    CouldntExtractEventData,
327    /// Contract doesn't support CES events.
328    ContractDoesntSupportEvents,
329    /// Tried to query event for a non-contract entity.
330    TriedToQueryEventForNonContract
331}
332
333/// Represents the result of a contract call.
334pub type OdraResult<T> = Result<T, OdraError>;
335
336impl From<CLValueError> for OdraError {
337    fn from(error: CLValueError) -> Self {
338        match error {
339            CLValueError::Serialization(_) => OdraError::VmError(Serialization),
340            CLValueError::Type(cl_type_mismatch) => OdraError::VmError(VmError::TypeMismatch {
341                expected: cl_type_mismatch.expected.clone(),
342                found: cl_type_mismatch.found.clone()
343            })
344        }
345    }
346}
347
348impl From<BytesReprError> for OdraError {
349    fn from(error: BytesReprError) -> Self {
350        match error {
351            BytesReprError::EarlyEndOfStream => ExecutionError::EarlyEndOfStream,
352            BytesReprError::Formatting => ExecutionError::Formatting,
353            BytesReprError::LeftOverBytes => ExecutionError::LeftOverBytes,
354            BytesReprError::OutOfMemory => ExecutionError::OutOfMemory,
355            BytesReprError::NotRepresentable => ExecutionError::NotRepresentable,
356            BytesReprError::ExceededRecursionDepth => ExecutionError::ExceededRecursionDepth,
357            _ => ExecutionError::Formatting
358        }
359        .into()
360    }
361}
362
363impl From<anyhow::Error> for OdraError {
364    fn from(value: anyhow::Error) -> Self {
365        OdraError::VmError(VmError::Other(value.to_string()))
366    }
367}