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