cosmwasm_vm/errors/
vm_error.rs

1use super::{impl_from_err, BT};
2use std::fmt::{Debug, Display};
3use thiserror::Error;
4
5use cosmwasm_crypto::CryptoError;
6
7use super::communication_error::CommunicationError;
8use crate::backend::BackendError;
9
10#[derive(Error, Debug)]
11#[non_exhaustive]
12pub enum VmError {
13    #[error("Aborted: {}", msg)]
14    Aborted { msg: String, backtrace: BT },
15    #[error("Error calling into the VM's backend: {}", source)]
16    BackendErr { source: BackendError, backtrace: BT },
17    #[error("Cache error: {msg}")]
18    CacheErr { msg: String, backtrace: BT },
19    #[error("Error in guest/host communication: {source}")]
20    CommunicationErr {
21        source: CommunicationError,
22        backtrace: BT,
23    },
24    #[error("Error compiling Wasm: {msg}")]
25    CompileErr { msg: String, backtrace: BT },
26    #[error("Couldn't convert from {} to {}. Input: {}", from_type, to_type, input)]
27    ConversionErr {
28        from_type: String,
29        to_type: String,
30        input: String,
31        backtrace: BT,
32    },
33    #[error("Crypto error: {}", source)]
34    CryptoErr { source: CryptoError, backtrace: BT },
35    #[error("Ran out of gas during contract execution")]
36    GasDepletion { backtrace: BT },
37    /// Whenever there is no specific error type available
38    #[error("Generic error: {msg}")]
39    GenericErr { msg: String, backtrace: BT },
40    #[error("Error instantiating a Wasm module: {msg}")]
41    InstantiationErr { msg: String, backtrace: BT },
42    #[error("Hash doesn't match stored data")]
43    IntegrityErr { backtrace: BT },
44    #[error("Error parsing into type {target_type}: {msg}")]
45    ParseErr {
46        /// the target type that was attempted
47        target_type: String,
48        msg: String,
49        backtrace: BT,
50    },
51    #[error("Data too long for deserialization. Got: {length} bytes; limit: {max_length} bytes")]
52    DeserializationLimitExceeded {
53        /// the target type that was attempted
54        length: usize,
55        max_length: usize,
56        backtrace: BT,
57    },
58    #[error("Error serializing type {source_type}: {msg}")]
59    SerializeErr {
60        /// the source type that was attempted
61        source_type: String,
62        msg: String,
63        backtrace: BT,
64    },
65    #[error("Error resolving Wasm function: {}", msg)]
66    ResolveErr { msg: String, backtrace: BT },
67    #[error(
68        "Unexpected number of result values when calling '{}'. Expected: {}, actual: {}.",
69        function_name,
70        expected,
71        actual
72    )]
73    ResultMismatch {
74        function_name: String,
75        expected: usize,
76        actual: usize,
77        backtrace: BT,
78    },
79    #[error("Error executing Wasm: {}", msg)]
80    RuntimeErr { msg: String, backtrace: BT },
81    #[error("Error during static Wasm validation: {}", msg)]
82    StaticValidationErr { msg: String, backtrace: BT },
83    #[error("Uninitialized Context Data: {}", kind)]
84    UninitializedContextData { kind: String, backtrace: BT },
85    #[error("Must not call a writing storage function in this context.")]
86    WriteAccessDenied { backtrace: BT },
87    #[error("Maximum call depth exceeded.")]
88    MaxCallDepthExceeded { backtrace: BT },
89    #[error(
90        "The called function args arity does not match. The contract's method arity: {}",
91        contract_method_arity
92    )]
93    FunctionArityMismatch {
94        contract_method_arity: usize,
95        backtrace: BT,
96    },
97}
98
99impl VmError {
100    pub(crate) fn aborted(msg: impl Into<String>) -> Self {
101        VmError::Aborted {
102            msg: msg.into(),
103            backtrace: BT::capture(),
104        }
105    }
106
107    pub(crate) fn backend_err(original: BackendError) -> Self {
108        VmError::BackendErr {
109            source: original,
110            backtrace: BT::capture(),
111        }
112    }
113
114    pub(crate) fn cache_err(msg: impl Into<String>) -> Self {
115        VmError::CacheErr {
116            msg: msg.into(),
117            backtrace: BT::capture(),
118        }
119    }
120
121    pub(crate) fn compile_err(msg: impl Into<String>) -> Self {
122        VmError::CompileErr {
123            msg: msg.into(),
124            backtrace: BT::capture(),
125        }
126    }
127
128    pub(crate) fn conversion_err(
129        from_type: impl Into<String>,
130        to_type: impl Into<String>,
131        input: impl Into<String>,
132    ) -> Self {
133        VmError::ConversionErr {
134            from_type: from_type.into(),
135            to_type: to_type.into(),
136            input: input.into(),
137            backtrace: BT::capture(),
138        }
139    }
140
141    pub(crate) fn crypto_err(original: CryptoError) -> Self {
142        VmError::CryptoErr {
143            source: original,
144            backtrace: BT::capture(),
145        }
146    }
147
148    pub(crate) fn gas_depletion() -> Self {
149        VmError::GasDepletion {
150            backtrace: BT::capture(),
151        }
152    }
153
154    pub(crate) fn generic_err(msg: impl Into<String>) -> Self {
155        VmError::GenericErr {
156            msg: msg.into(),
157            backtrace: BT::capture(),
158        }
159    }
160
161    pub(crate) fn instantiation_err(msg: impl Into<String>) -> Self {
162        VmError::InstantiationErr {
163            msg: msg.into(),
164            backtrace: BT::capture(),
165        }
166    }
167
168    pub(crate) fn integrity_err() -> Self {
169        VmError::IntegrityErr {
170            backtrace: BT::capture(),
171        }
172    }
173
174    pub(crate) fn parse_err(target: impl Into<String>, msg: impl Display) -> Self {
175        VmError::ParseErr {
176            target_type: target.into(),
177            msg: msg.to_string(),
178            backtrace: BT::capture(),
179        }
180    }
181
182    pub(crate) fn deserialization_limit_exceeded(length: usize, max_length: usize) -> Self {
183        VmError::DeserializationLimitExceeded {
184            length,
185            max_length,
186            backtrace: BT::capture(),
187        }
188    }
189
190    pub(crate) fn serialize_err(source: impl Into<String>, msg: impl Display) -> Self {
191        VmError::SerializeErr {
192            source_type: source.into(),
193            msg: msg.to_string(),
194            backtrace: BT::capture(),
195        }
196    }
197
198    pub(crate) fn resolve_err(msg: impl Into<String>) -> Self {
199        VmError::ResolveErr {
200            msg: msg.into(),
201            backtrace: BT::capture(),
202        }
203    }
204
205    pub(crate) fn result_mismatch(
206        function_name: impl Into<String>,
207        expected: usize,
208        actual: usize,
209    ) -> Self {
210        VmError::ResultMismatch {
211            function_name: function_name.into(),
212            expected,
213            actual,
214            backtrace: BT::capture(),
215        }
216    }
217
218    // Creates a runtime error with the given message.
219    // This is private since it is only needed when converting wasmer::RuntimeError
220    // to VmError.
221    fn runtime_err(msg: impl Into<String>) -> Self {
222        VmError::RuntimeErr {
223            msg: msg.into(),
224            backtrace: BT::capture(),
225        }
226    }
227
228    pub(crate) fn static_validation_err(msg: impl Into<String>) -> Self {
229        VmError::StaticValidationErr {
230            msg: msg.into(),
231            backtrace: BT::capture(),
232        }
233    }
234
235    pub(crate) fn uninitialized_context_data(kind: impl Into<String>) -> Self {
236        VmError::UninitializedContextData {
237            kind: kind.into(),
238            backtrace: BT::capture(),
239        }
240    }
241
242    pub(crate) fn write_access_denied() -> Self {
243        VmError::WriteAccessDenied {
244            backtrace: BT::capture(),
245        }
246    }
247
248    pub(crate) fn max_call_depth_exceeded() -> Self {
249        VmError::MaxCallDepthExceeded {
250            backtrace: BT::capture(),
251        }
252    }
253
254    pub(crate) fn function_arity_mismatch(contract_method_arity: usize) -> Self {
255        VmError::FunctionArityMismatch {
256            contract_method_arity,
257            backtrace: BT::capture(),
258        }
259    }
260}
261
262impl_from_err!(CommunicationError, VmError, VmError::CommunicationErr);
263
264impl From<BackendError> for VmError {
265    fn from(original: BackendError) -> Self {
266        match original {
267            BackendError::OutOfGas {} => VmError::gas_depletion(),
268            _ => VmError::backend_err(original),
269        }
270    }
271}
272
273impl From<CryptoError> for VmError {
274    fn from(original: CryptoError) -> Self {
275        VmError::crypto_err(original)
276    }
277}
278
279impl From<wasmer::wasmparser::BinaryReaderError> for VmError {
280    fn from(original: wasmer::wasmparser::BinaryReaderError) -> Self {
281        VmError::static_validation_err(format!(
282            "Wasm bytecode could not be deserialized. Deserialization error: \"{original}\""
283        ))
284    }
285}
286
287impl From<wasmer::ExportError> for VmError {
288    fn from(original: wasmer::ExportError) -> Self {
289        VmError::resolve_err(format!("Could not get export: {original}"))
290    }
291}
292
293impl From<wasmer::SerializeError> for VmError {
294    fn from(original: wasmer::SerializeError) -> Self {
295        VmError::cache_err(format!("Could not serialize module: {original}"))
296    }
297}
298
299impl From<wasmer::DeserializeError> for VmError {
300    fn from(original: wasmer::DeserializeError) -> Self {
301        VmError::cache_err(format!("Could not deserialize module: {original}"))
302    }
303}
304
305impl From<wasmer::RuntimeError> for VmError {
306    fn from(original: wasmer::RuntimeError) -> Self {
307        // Do not use the Display implementation or to_string() of `RuntimeError`
308        // because it can contain a system specific stack trace, which can
309        // lead to non-deterministic execution.
310        //
311        // Implementation follows https://github.com/wasmerio/wasmer/blob/2.0.0/lib/engine/src/trap/error.rs#L215
312        let message = format!("RuntimeError: {}", original.message());
313        debug_assert!(
314            original.to_string().starts_with(&message),
315            "The error message we created is not a prefix of the error message from Wasmer. Our message: '{}'. Wasmer message: '{}'",
316            &message,
317            original
318        );
319        VmError::runtime_err(format!("Wasmer runtime error: {}", &message))
320    }
321}
322
323impl From<wasmer::CompileError> for VmError {
324    fn from(original: wasmer::CompileError) -> Self {
325        VmError::compile_err(format!("Could not compile: {original}"))
326    }
327}
328
329impl From<std::convert::Infallible> for VmError {
330    fn from(_original: std::convert::Infallible) -> Self {
331        unreachable!();
332    }
333}
334
335impl From<VmError> for wasmer::RuntimeError {
336    fn from(original: VmError) -> wasmer::RuntimeError {
337        let msg: String = original.to_string();
338        wasmer::RuntimeError::new(msg)
339    }
340}
341
342#[cfg(test)]
343mod tests {
344    use super::*;
345
346    // constructors
347
348    #[test]
349    fn backend_err_works() {
350        let error = VmError::backend_err(BackendError::unknown("something went wrong"));
351        match error {
352            VmError::BackendErr {
353                source: BackendError::Unknown { msg },
354                ..
355            } => assert_eq!(msg, "something went wrong"),
356            e => panic!("Unexpected error: {e:?}"),
357        }
358    }
359
360    #[test]
361    fn cache_err_works() {
362        let error = VmError::cache_err("something went wrong");
363        match error {
364            VmError::CacheErr { msg, .. } => assert_eq!(msg, "something went wrong"),
365            e => panic!("Unexpected error: {e:?}"),
366        }
367    }
368
369    #[test]
370    fn compile_err_works() {
371        let error = VmError::compile_err("something went wrong");
372        match error {
373            VmError::CompileErr { msg, .. } => assert_eq!(msg, "something went wrong"),
374            e => panic!("Unexpected error: {e:?}"),
375        }
376    }
377
378    #[test]
379    fn conversion_err_works() {
380        let error = VmError::conversion_err("i32", "u32", "-9");
381        match error {
382            VmError::ConversionErr {
383                from_type,
384                to_type,
385                input,
386                ..
387            } => {
388                assert_eq!(from_type, "i32");
389                assert_eq!(to_type, "u32");
390                assert_eq!(input, "-9");
391            }
392            e => panic!("Unexpected error: {e:?}"),
393        }
394    }
395
396    #[test]
397    fn crypto_err_works() {
398        let error = VmError::crypto_err(CryptoError::generic_err("something went wrong"));
399        match error {
400            VmError::CryptoErr {
401                source: CryptoError::GenericErr { msg, .. },
402                ..
403            } => assert_eq!(msg, "something went wrong"),
404            e => panic!("Unexpected error: {e:?}"),
405        }
406    }
407
408    #[test]
409    fn gas_depletion_works() {
410        let error = VmError::gas_depletion();
411        match error {
412            VmError::GasDepletion { .. } => {}
413            e => panic!("Unexpected error: {e:?}"),
414        }
415    }
416
417    #[test]
418    fn generic_err_works() {
419        let guess = 7;
420        let error = VmError::generic_err(format!("{guess} is too low"));
421        match error {
422            VmError::GenericErr { msg, .. } => {
423                assert_eq!(msg, String::from("7 is too low"));
424            }
425            e => panic!("Unexpected error: {e:?}"),
426        }
427    }
428
429    #[test]
430    fn instantiation_err_works() {
431        let error = VmError::instantiation_err("something went wrong");
432        match error {
433            VmError::InstantiationErr { msg, .. } => assert_eq!(msg, "something went wrong"),
434            e => panic!("Unexpected error: {e:?}"),
435        }
436    }
437
438    #[test]
439    fn integrity_err_works() {
440        let error = VmError::integrity_err();
441        match error {
442            VmError::IntegrityErr { .. } => {}
443            e => panic!("Unexpected error: {e:?}"),
444        }
445    }
446
447    #[test]
448    fn parse_err_works() {
449        let error = VmError::parse_err("Book", "Missing field: title");
450        match error {
451            VmError::ParseErr {
452                target_type, msg, ..
453            } => {
454                assert_eq!(target_type, "Book");
455                assert_eq!(msg, "Missing field: title");
456            }
457            e => panic!("Unexpected error: {e:?}"),
458        }
459    }
460
461    #[test]
462    fn serialize_err_works() {
463        let error = VmError::serialize_err("Book", "Content too long");
464        match error {
465            VmError::SerializeErr {
466                source_type, msg, ..
467            } => {
468                assert_eq!(source_type, "Book");
469                assert_eq!(msg, "Content too long");
470            }
471            e => panic!("Unexpected error: {e:?}"),
472        }
473    }
474
475    #[test]
476    fn resolve_err_works() {
477        let error = VmError::resolve_err("function has different signature");
478        match error {
479            VmError::ResolveErr { msg, .. } => assert_eq!(msg, "function has different signature"),
480            e => panic!("Unexpected error: {e:?}"),
481        }
482    }
483
484    #[test]
485    fn result_mismatch_works() {
486        let error = VmError::result_mismatch("action", 0, 1);
487        match error {
488            VmError::ResultMismatch {
489                function_name,
490                expected,
491                actual,
492                ..
493            } => {
494                assert_eq!(function_name, "action");
495                assert_eq!(expected, 0);
496                assert_eq!(actual, 1);
497            }
498            e => panic!("Unexpected error: {e:?}"),
499        }
500    }
501
502    #[test]
503    fn runtime_err_works() {
504        let error = VmError::runtime_err("something went wrong");
505        match error {
506            VmError::RuntimeErr { msg, .. } => assert_eq!(msg, "something went wrong"),
507            e => panic!("Unexpected error: {e:?}"),
508        }
509    }
510
511    #[test]
512    fn static_validation_err_works() {
513        let error = VmError::static_validation_err("export xy missing");
514        match error {
515            VmError::StaticValidationErr { msg, .. } => assert_eq!(msg, "export xy missing"),
516            e => panic!("Unexpected error: {e:?}"),
517        }
518    }
519
520    #[test]
521    fn uninitialized_context_data_works() {
522        let error = VmError::uninitialized_context_data("foo");
523        match error {
524            VmError::UninitializedContextData { kind, .. } => assert_eq!(kind, "foo"),
525            e => panic!("Unexpected error: {e:?}"),
526        }
527    }
528
529    #[test]
530    fn write_access_denied() {
531        let error = VmError::write_access_denied();
532        match error {
533            VmError::WriteAccessDenied { .. } => {}
534            e => panic!("Unexpected error: {e:?}"),
535        }
536    }
537}