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 #[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 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 length: usize,
55 max_length: usize,
56 backtrace: BT,
57 },
58 #[error("Error serializing type {source_type}: {msg}")]
59 SerializeErr {
60 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 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 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 #[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}