1use borsh::{BorshDeserialize, BorshSerialize};
2use std::any::Any;
3use std::fmt::{self, Error, Formatter};
4use std::io;
5
6#[derive(Debug, thiserror::Error)]
11pub enum VMRunnerError {
12 #[error("{0}")]
15 InconsistentStateError(InconsistentStateError),
16 #[error("cache error: {0}")]
18 CacheError(#[from] CacheError),
19 #[error("loading error: {0}")]
22 LoadingError(String),
23 #[error("external error")]
25 ExternalError(AnyError),
26 #[error("non-deterministic error during contract execution: {0}")]
28 Nondeterministic(String),
29 #[error("unknown error during contract execution: {debug_message}")]
30 WasmUnknownError { debug_message: String },
31}
32
33#[derive(Debug, PartialEq, Eq, strum::IntoStaticStr)]
42pub enum FunctionCallError {
43 CompilationError(CompilationError),
45 LinkError {
47 msg: String,
48 },
49 MethodResolveError(MethodResolveError),
51 WasmTrap(WasmTrap),
53 HostError(HostError),
54}
55
56#[derive(Debug, thiserror::Error, strum::IntoStaticStr)]
57pub enum CacheError {
58 #[error("cache read error")]
59 ReadError(#[source] io::Error),
60 #[error("cache write error")]
61 WriteError(#[source] io::Error),
62 #[error("cache deserialization error")]
63 DeserializationError,
64 #[error("cache serialization error")]
65 SerializationError { hash: [u8; 32] },
66}
67#[derive(Debug, Clone, PartialEq, Eq, strum::IntoStaticStr)]
69pub enum WasmTrap {
70 Unreachable,
72 IncorrectCallIndirectSignature,
74 MemoryOutOfBounds,
76 CallIndirectOOB,
78 IllegalArithmetic,
80 MisalignedAtomicAccess,
82 IndirectCallToNull,
84 StackOverflow,
86 GenericTrap,
88}
89
90#[derive(Debug, Clone, PartialEq, Eq, strum::IntoStaticStr)]
91pub enum MethodResolveError {
92 MethodEmptyName,
93 MethodNotFound,
94 MethodInvalidSignature,
95}
96
97#[derive(Debug, Clone, PartialEq, Eq, BorshDeserialize, BorshSerialize, strum::IntoStaticStr)]
98pub enum CompilationError {
99 CodeDoesNotExist {
100 account_id: Box<str>,
101 },
102 PrepareError(PrepareError),
103 WasmerCompileError {
107 msg: String,
108 },
109}
110
111#[derive(Debug, Clone, PartialEq, Eq, BorshDeserialize, BorshSerialize)]
112pub enum PrepareError {
114 Serialization,
116 Deserialization,
118 InternalMemoryDeclared,
120 GasInstrumentation,
124 StackHeightInstrumentation,
128 Instantiate,
133 Memory,
135 TooManyFunctions,
137 TooManyLocals,
139}
140
141#[derive(Debug, Clone, PartialEq, Eq, strum::IntoStaticStr)]
142pub enum HostError {
143 BadUTF16,
145 BadUTF8,
147 GasExceeded,
149 GasLimitExceeded,
151 BalanceExceeded,
153 EmptyMethodName,
155 GuestPanic { panic_msg: String },
157 IntegerOverflow,
159 InvalidPromiseIndex { promise_idx: u64 },
161 CannotAppendActionToJointPromise,
163 CannotReturnJointPromise,
165 InvalidPromiseResultIndex { result_idx: u64 },
167 InvalidRegisterId { register_id: u64 },
169 MemoryAccessViolation,
171 InvalidReceiptIndex { receipt_index: u64 },
173 InvalidIteratorIndex { iterator_index: u64 },
175 InvalidAccountId,
177 InvalidMethodName,
179 InvalidPublicKey,
181 ProhibitedInView { method_name: String },
183 NumberOfLogsExceeded { limit: u64 },
185 KeyLengthExceeded { length: u64, limit: u64 },
187 ValueLengthExceeded { length: u64, limit: u64 },
189 TotalLogLengthExceeded { length: u64, limit: u64 },
191 NumberPromisesExceeded { number_of_promises: u64, limit: u64 },
193 NumberInputDataDependenciesExceeded { number_of_input_data_dependencies: u64, limit: u64 },
195 ReturnedValueLengthExceeded { length: u64, limit: u64 },
197 ContractSizeExceeded { size: u64, limit: u64 },
199 Deprecated { method_name: String },
201 ECRecoverError { msg: String },
203 AltBn128InvalidInput { msg: String },
206 Ed25519VerifyInvalidInput { msg: String },
209}
210
211#[derive(Debug, PartialEq, Eq)]
212pub enum VMLogicError {
213 HostError(HostError),
215 ExternalError(AnyError),
217 InconsistentStateError(InconsistentStateError),
219}
220
221impl std::error::Error for VMLogicError {}
222
223#[derive(Debug, Clone, PartialEq, Eq)]
226pub enum InconsistentStateError {
227 IntegerOverflow,
229}
230
231impl From<HostError> for VMLogicError {
232 fn from(err: HostError) -> Self {
233 VMLogicError::HostError(err)
234 }
235}
236
237impl From<InconsistentStateError> for VMLogicError {
238 fn from(err: InconsistentStateError) -> Self {
239 VMLogicError::InconsistentStateError(err)
240 }
241}
242
243impl From<PrepareError> for FunctionCallError {
244 fn from(err: PrepareError) -> Self {
245 FunctionCallError::CompilationError(CompilationError::PrepareError(err))
246 }
247}
248
249impl TryFrom<VMLogicError> for FunctionCallError {
258 type Error = VMRunnerError;
259 fn try_from(err: VMLogicError) -> Result<Self, Self::Error> {
260 match err {
261 VMLogicError::HostError(h) => Ok(FunctionCallError::HostError(h)),
262 VMLogicError::ExternalError(s) => Err(VMRunnerError::ExternalError(s)),
263 VMLogicError::InconsistentStateError(e) => {
264 Err(VMRunnerError::InconsistentStateError(e))
265 }
266 }
267 }
268}
269
270impl fmt::Display for VMLogicError {
271 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
272 write!(f, "{:?}", self)
273 }
274}
275
276impl fmt::Display for PrepareError {
277 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
278 use PrepareError::*;
279 f.write_str(match self {
280 Serialization => "Error happened while serializing the module.",
281 Deserialization => "Error happened while deserializing the module.",
282 InternalMemoryDeclared => "Internal memory declaration has been found in the module.",
283 GasInstrumentation => "Gas instrumentation failed.",
284 StackHeightInstrumentation => "Stack instrumentation failed.",
285 Instantiate => "Error happened during instantiation.",
286 Memory => "Error creating memory.",
287 TooManyFunctions => "Too many functions in contract.",
288 TooManyLocals => "Too many locals declared in the contract.",
289 })
290 }
291}
292
293impl fmt::Display for FunctionCallError {
294 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
295 match self {
296 FunctionCallError::CompilationError(e) => e.fmt(f),
297 FunctionCallError::MethodResolveError(e) => e.fmt(f),
298 FunctionCallError::HostError(e) => e.fmt(f),
299 FunctionCallError::LinkError { msg } => write!(f, "{}", msg),
300 FunctionCallError::WasmTrap(trap) => write!(f, "WebAssembly trap: {}", trap),
301 }
302 }
303}
304
305impl fmt::Display for WasmTrap {
306 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
307 match self {
308 WasmTrap::Unreachable => write!(f, "An `unreachable` opcode was executed."),
309 WasmTrap::IncorrectCallIndirectSignature => {
310 write!(f, "Call indirect incorrect signature trap.")
311 }
312 WasmTrap::MemoryOutOfBounds => write!(f, "Memory out of bounds trap."),
313 WasmTrap::CallIndirectOOB => write!(f, "Call indirect out of bounds trap."),
314 WasmTrap::IllegalArithmetic => {
315 write!(f, "An arithmetic exception, e.g. divided by zero.")
316 }
317 WasmTrap::MisalignedAtomicAccess => write!(f, "Misaligned atomic access trap."),
318 WasmTrap::GenericTrap => write!(f, "Generic trap."),
319 WasmTrap::StackOverflow => write!(f, "Stack overflow."),
320 WasmTrap::IndirectCallToNull => write!(f, "Indirect call to null."),
321 }
322 }
323}
324
325impl fmt::Display for CompilationError {
326 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
327 match self {
328 CompilationError::CodeDoesNotExist { account_id } => {
329 write!(f, "cannot find contract code for account {}", account_id)
330 }
331 CompilationError::PrepareError(p) => write!(f, "PrepareError: {}", p),
332 CompilationError::WasmerCompileError { msg } => {
333 write!(f, "Wasmer compilation error: {}", msg)
334 }
335 }
336 }
337}
338
339impl fmt::Display for MethodResolveError {
340 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
341 fmt::Debug::fmt(self, f)
342 }
343}
344
345impl std::fmt::Display for InconsistentStateError {
346 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
347 match self {
348 InconsistentStateError::IntegerOverflow => write!(
349 f,
350 "Math operation with a value from the state resulted in a integer overflow.",
351 ),
352 }
353 }
354}
355
356impl std::fmt::Display for HostError {
357 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
358 use HostError::*;
359 match self {
360 BadUTF8 => write!(f, "String encoding is bad UTF-8 sequence."),
361 BadUTF16 => write!(f, "String encoding is bad UTF-16 sequence."),
362 GasExceeded => write!(f, "Exceeded the prepaid gas."),
363 GasLimitExceeded => {
364 write!(f, "Exceeded the maximum amount of gas allowed to burn per contract.")
365 }
366 BalanceExceeded => write!(f, "Exceeded the account balance."),
367 EmptyMethodName => write!(f, "Tried to call an empty method name."),
368 GuestPanic { panic_msg } => write!(f, "Smart contract panicked: {}", panic_msg),
369 IntegerOverflow => write!(f, "Integer overflow."),
370 InvalidIteratorIndex { iterator_index } => {
371 write!(f, "Iterator index {:?} does not exist", iterator_index)
372 }
373 InvalidPromiseIndex { promise_idx } => {
374 write!(f, "{:?} does not correspond to existing promises", promise_idx)
375 }
376 CannotAppendActionToJointPromise => {
377 write!(f, "Actions can only be appended to non-joint promise.")
378 }
379 CannotReturnJointPromise => {
380 write!(f, "Returning joint promise is currently prohibited.")
381 }
382 InvalidPromiseResultIndex { result_idx } => {
383 write!(f, "Accessed invalid promise result index: {:?}", result_idx)
384 }
385 InvalidRegisterId { register_id } => {
386 write!(f, "Accessed invalid register id: {:?}", register_id)
387 }
388 MemoryAccessViolation => write!(f, "Accessed memory outside the bounds."),
389 InvalidReceiptIndex { receipt_index } => {
390 write!(f, "VM Logic returned an invalid receipt index: {:?}", receipt_index)
391 }
392 InvalidAccountId => write!(f, "VM Logic returned an invalid account id"),
393 InvalidMethodName => write!(f, "VM Logic returned an invalid method name"),
394 InvalidPublicKey => write!(f, "VM Logic provided an invalid public key"),
395 ProhibitedInView { method_name } => {
396 write!(f, "{} is not allowed in view calls", method_name)
397 }
398 NumberOfLogsExceeded { limit } => {
399 write!(f, "The number of logs will exceed the limit {}", limit)
400 }
401 KeyLengthExceeded { length, limit } => {
402 write!(f, "The length of a storage key {} exceeds the limit {}", length, limit)
403 }
404 ValueLengthExceeded { length, limit } => {
405 write!(f, "The length of a storage value {} exceeds the limit {}", length, limit)
406 }
407 TotalLogLengthExceeded { length, limit } => {
408 write!(f, "The length of a log message {} exceeds the limit {}", length, limit)
409 }
410 NumberPromisesExceeded { number_of_promises, limit } => write!(
411 f,
412 "The number of promises within a FunctionCall {} exceeds the limit {}",
413 number_of_promises, limit
414 ),
415 NumberInputDataDependenciesExceeded { number_of_input_data_dependencies, limit } => {
416 write!(
417 f,
418 "The number of input data dependencies {} exceeds the limit {}",
419 number_of_input_data_dependencies, limit
420 )
421 }
422 ReturnedValueLengthExceeded { length, limit } => {
423 write!(f, "The length of a returned value {} exceeds the limit {}", length, limit)
424 }
425 ContractSizeExceeded { size, limit } => write!(
426 f,
427 "The size of a contract code in DeployContract action {} exceeds the limit {}",
428 size, limit
429 ),
430 Deprecated { method_name } => {
431 write!(f, "Attempted to call deprecated host function {}", method_name)
432 }
433 AltBn128InvalidInput { msg } => write!(f, "AltBn128 invalid input: {}", msg),
434 ECRecoverError { msg } => write!(f, "ECDSA recover error: {}", msg),
435 Ed25519VerifyInvalidInput { msg } => {
436 write!(f, "ED25519 signature verification error: {}", msg)
437 }
438 }
439 }
440}
441
442pub struct AnyError {
448 any: Box<dyn AnyEq>,
449}
450
451#[derive(Debug, thiserror::Error)]
452#[error("failed to downcast")]
453pub struct DowncastFailedError;
454
455impl AnyError {
456 pub fn new<E: Any + Eq + Send + Sync + 'static>(err: E) -> AnyError {
457 AnyError { any: Box::new(err) }
458 }
459 pub fn downcast<E: Any + Eq + Send + Sync + 'static>(self) -> Result<E, DowncastFailedError> {
460 match self.any.into_any().downcast::<E>() {
461 Ok(it) => Ok(*it),
462 Err(_) => Err(DowncastFailedError),
463 }
464 }
465}
466
467impl PartialEq for AnyError {
468 fn eq(&self, other: &Self) -> bool {
469 self.any.any_eq(&*other.any)
470 }
471}
472
473impl Eq for AnyError {}
474
475impl fmt::Debug for AnyError {
476 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
477 fmt::Debug::fmt(self.any.as_any(), f)
478 }
479}
480
481trait AnyEq: Any + Send + Sync {
482 fn any_eq(&self, rhs: &dyn AnyEq) -> bool;
483 fn as_any(&self) -> &dyn Any;
484 fn into_any(self: Box<Self>) -> Box<dyn Any>;
485}
486
487impl<T: Any + Eq + Sized + Send + Sync> AnyEq for T {
488 fn any_eq(&self, rhs: &dyn AnyEq) -> bool {
489 match rhs.as_any().downcast_ref::<Self>() {
490 Some(rhs) => self == rhs,
491 None => false,
492 }
493 }
494 fn as_any(&self) -> &dyn Any {
495 self
496 }
497 fn into_any(self: Box<Self>) -> Box<dyn Any> {
498 self
499 }
500}
501
502#[cfg(test)]
503mod tests {
504 use crate::logic::errors::{
505 CompilationError, FunctionCallError, MethodResolveError, PrepareError,
506 };
507
508 #[test]
509 fn test_display() {
510 assert_eq!(
512 FunctionCallError::MethodResolveError(MethodResolveError::MethodInvalidSignature)
513 .to_string(),
514 "MethodInvalidSignature"
515 );
516 assert_eq!(
517 FunctionCallError::CompilationError(CompilationError::PrepareError(
518 PrepareError::StackHeightInstrumentation
519 ))
520 .to_string(),
521 "PrepareError: Stack instrumentation failed."
522 );
523 }
524}