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