1use crate::xdr::{ScError, ScErrorCode, ScErrorType, ScVal};
2use crate::{
3 impl_wrapper_as_and_to_val, impl_wrapper_tag_based_constructors,
4 impl_wrapper_tag_based_valconvert, impl_wrapper_wasmi_conversions, Compare, ConversionError,
5 Env, SymbolError, Val,
6};
7use core::{
8 cmp::Ordering,
9 fmt::Debug,
10 hash::{Hash, Hasher},
11};
12
13#[repr(transparent)]
19#[derive(Copy, Clone)]
20pub struct Error(Val);
21
22impl_wrapper_tag_based_valconvert!(Error);
23impl_wrapper_tag_based_constructors!(Error);
24impl_wrapper_as_and_to_val!(Error);
25impl_wrapper_wasmi_conversions!(Error);
26
27impl Hash for Error {
28 #[inline(always)]
29 fn hash<H: Hasher>(&self, state: &mut H) {
30 self.as_val().get_payload().hash(state);
31 }
32}
33
34impl PartialEq for Error {
35 #[inline(always)]
36 fn eq(&self, other: &Self) -> bool {
37 self.as_val().get_payload() == other.as_val().get_payload()
38 }
39}
40
41impl Eq for Error {}
42
43impl PartialOrd for Error {
44 #[inline(always)]
45 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
46 Some(self.cmp(other))
47 }
48}
49
50impl Ord for Error {
51 #[inline(always)]
52 fn cmp(&self, other: &Self) -> Ordering {
53 let self_tup = (self.as_val().get_minor(), self.as_val().get_major());
54 let other_tup = (other.as_val().get_minor(), other.as_val().get_major());
55 self_tup.cmp(&other_tup)
56 }
57}
58
59impl<E: Env> Compare<Error> for E {
60 type Error = E::Error;
61 fn compare(&self, a: &Error, b: &Error) -> Result<Ordering, Self::Error> {
62 Ok(a.cmp(b))
63 }
64}
65
66impl Debug for Error {
67 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
68 let (min, maj) = (
69 self.as_val().get_minor() as i32,
70 self.as_val().get_major() as i32,
71 );
72 if let Ok(type_) = ScErrorType::try_from(min) {
73 if type_ == ScErrorType::Contract {
74 write!(f, "Error({}, #{})", type_.name(), maj)
75 } else if let Ok(code) = ScErrorCode::try_from(maj) {
76 write!(f, "Error({}, {})", type_.name(), code.name())
77 } else {
78 write!(f, "Error({}, #{})", type_.name(), maj)
79 }
80 } else {
81 if let Ok(code) = ScErrorCode::try_from(maj) {
82 write!(f, "Error(#{}, {})", min, code.name())
83 } else {
84 write!(f, "Error(#{}, #{})", min, maj)
85 }
86 }
87 }
88}
89
90impl<'a> From<&'a Error> for Error {
91 fn from(value: &'a Error) -> Self {
92 *value
93 }
94}
95
96impl TryFrom<Error> for ScError {
97 type Error = crate::xdr::Error;
98 fn try_from(er: Error) -> Result<Self, Self::Error> {
99 let type_: ScErrorType = (er.as_val().get_minor() as i32).try_into()?;
100 let u: u32 = er.as_val().get_major();
101 Ok(match type_ {
102 ScErrorType::Contract => ScError::Contract(u),
103 ScErrorType::WasmVm => ScError::WasmVm((u as i32).try_into()?),
104 ScErrorType::Context => ScError::Context((u as i32).try_into()?),
105 ScErrorType::Storage => ScError::Storage((u as i32).try_into()?),
106 ScErrorType::Object => ScError::Object((u as i32).try_into()?),
107 ScErrorType::Crypto => ScError::Crypto((u as i32).try_into()?),
108 ScErrorType::Events => ScError::Events((u as i32).try_into()?),
109 ScErrorType::Budget => ScError::Budget((u as i32).try_into()?),
110 ScErrorType::Value => ScError::Value((u as i32).try_into()?),
111 ScErrorType::Auth => ScError::Auth((u as i32).try_into()?),
112 })
113 }
114}
115
116impl TryFrom<Error> for ScVal {
117 type Error = crate::xdr::Error;
118 fn try_from(st: Error) -> Result<Self, crate::xdr::Error> {
119 Ok(ScVal::Error(<_ as TryInto<ScError>>::try_into(st)?))
120 }
121}
122
123impl TryFrom<&Error> for ScVal {
124 type Error = crate::xdr::Error;
125 fn try_from(value: &Error) -> Result<Self, crate::xdr::Error> {
126 (*value).try_into()
127 }
128}
129
130impl From<ScError> for Error {
131 fn from(er: ScError) -> Self {
132 Error::from_scerror(er)
133 }
134}
135
136impl From<(ScErrorType, ScErrorCode)> for Error {
137 fn from(value: (ScErrorType, ScErrorCode)) -> Self {
138 Error::from_type_and_code(value.0, value.1)
139 }
140}
141
142impl From<SymbolError> for Error {
143 fn from(_: SymbolError) -> Self {
144 Error::from_type_and_code(ScErrorType::Value, ScErrorCode::InvalidInput)
145 }
146}
147
148impl From<ConversionError> for Error {
149 fn from(_: ConversionError) -> Self {
150 Error::from_type_and_code(ScErrorType::Value, ScErrorCode::UnexpectedType)
151 }
152}
153
154impl From<crate::xdr::Error> for Error {
155 fn from(e: crate::xdr::Error) -> Self {
156 match e {
157 crate::xdr::Error::DepthLimitExceeded | crate::xdr::Error::LengthLimitExceeded => {
158 Error::from_type_and_code(ScErrorType::Context, ScErrorCode::ExceededLimit)
159 }
160 _ => Error::from_type_and_code(ScErrorType::Value, ScErrorCode::InvalidInput),
161 }
162 }
163}
164
165impl From<Error> for crate::xdr::Error {
168 fn from(_value: Error) -> Self {
169 crate::xdr::Error::Unsupported
170 }
171}
172
173#[cfg(feature = "wasmi")]
174impl From<wasmi::core::TrapCode> for Error {
175 fn from(code: wasmi::core::TrapCode) -> Self {
176 let ec = match code {
177 wasmi::core::TrapCode::UnreachableCodeReached => ScErrorCode::InvalidAction,
178
179 wasmi::core::TrapCode::MemoryOutOfBounds | wasmi::core::TrapCode::TableOutOfBounds => {
180 ScErrorCode::IndexBounds
181 }
182
183 wasmi::core::TrapCode::IndirectCallToNull => ScErrorCode::MissingValue,
184
185 wasmi::core::TrapCode::IntegerDivisionByZero
186 | wasmi::core::TrapCode::IntegerOverflow
187 | wasmi::core::TrapCode::BadConversionToInteger => ScErrorCode::ArithDomain,
188
189 wasmi::core::TrapCode::BadSignature => ScErrorCode::UnexpectedType,
190
191 wasmi::core::TrapCode::StackOverflow
192 | wasmi::core::TrapCode::OutOfFuel
193 | wasmi::core::TrapCode::GrowthOperationLimited => {
194 return Error::from_type_and_code(ScErrorType::Budget, ScErrorCode::ExceededLimit)
195 }
196 };
197 return Error::from_type_and_code(ScErrorType::WasmVm, ec);
198 }
199}
200
201#[cfg(feature = "wasmi")]
202impl From<wasmi::errors::FuncError> for Error {
203 fn from(err: wasmi::errors::FuncError) -> Self {
204 let ec = match err {
205 wasmi::errors::FuncError::ExportedFuncNotFound => ScErrorCode::MissingValue,
206 wasmi::errors::FuncError::MismatchingParameterType
207 | wasmi::errors::FuncError::MismatchingResultType => ScErrorCode::UnexpectedType,
208 wasmi::errors::FuncError::MismatchingParameterLen
209 | wasmi::errors::FuncError::MismatchingResultLen => ScErrorCode::UnexpectedSize,
210 };
211 return Error::from_type_and_code(ScErrorType::WasmVm, ec);
212 }
213}
214
215#[cfg(feature = "wasmi")]
216impl From<wasmi::Error> for Error {
217 fn from(e: wasmi::Error) -> Self {
218 const EXCEEDED_LIMIT: Error =
219 Error::from_type_and_code(ScErrorType::Budget, ScErrorCode::ExceededLimit);
220 const INDEX_BOUND: Error =
221 Error::from_type_and_code(ScErrorType::WasmVm, ScErrorCode::IndexBounds);
222
223 match e {
224 wasmi::Error::Memory(e) => match e {
225 wasmi::errors::MemoryError::OutOfBoundsAllocation
226 | wasmi::errors::MemoryError::OutOfBoundsGrowth => return EXCEEDED_LIMIT,
227 wasmi::errors::MemoryError::OutOfBoundsAccess => return INDEX_BOUND,
228 _ => (),
229 },
230 wasmi::Error::Table(e) => match e {
231 wasmi::errors::TableError::GrowOutOfBounds { .. } => return EXCEEDED_LIMIT,
232 wasmi::errors::TableError::AccessOutOfBounds { .. }
233 | wasmi::errors::TableError::CopyOutOfBounds => return INDEX_BOUND,
234 _ => (),
235 },
236 wasmi::Error::Instantiation(e) => match e {
237 wasmi::errors::InstantiationError::Memory(me) => match me {
238 wasmi::errors::MemoryError::OutOfBoundsAllocation
239 | wasmi::errors::MemoryError::OutOfBoundsGrowth => return EXCEEDED_LIMIT,
240 wasmi::errors::MemoryError::OutOfBoundsAccess => return INDEX_BOUND,
241 _ => (),
242 },
243 wasmi::errors::InstantiationError::Table(te) => match te {
244 wasmi::errors::TableError::GrowOutOfBounds { .. } => return EXCEEDED_LIMIT,
245 wasmi::errors::TableError::AccessOutOfBounds { .. }
246 | wasmi::errors::TableError::CopyOutOfBounds => return INDEX_BOUND,
247 _ => (),
248 },
249 _ => (),
250 },
251 wasmi::Error::Store(e) => {
252 if let wasmi::errors::FuelError::OutOfFuel = e {
253 return EXCEEDED_LIMIT;
254 }
255 }
256 wasmi::Error::Trap(trap) => {
257 if let Some(code) = trap.trap_code() {
258 return code.into();
259 }
260 }
261 wasmi::Error::Func(e) => {
262 return e.into();
263 }
264 wasmi::Error::Global(e) => {
265 if matches!(e, wasmi::errors::GlobalError::TypeMismatch { .. }) {
266 return Error::from_type_and_code(
267 ScErrorType::WasmVm,
268 ScErrorCode::UnexpectedType,
269 );
270 }
271 }
272 _ => (),
273 }
274
275 Error::from_type_and_code(ScErrorType::WasmVm, ScErrorCode::InvalidAction)
276 }
277}
278
279#[cfg(feature = "wasmi")]
280impl From<wasmparser::BinaryReaderError> for Error {
281 fn from(_: wasmparser::BinaryReaderError) -> Self {
282 Error::from_type_and_code(ScErrorType::WasmVm, ScErrorCode::InvalidInput)
283 }
284}
285
286impl Error {
287 #[inline(always)]
291 pub const fn is_type(&self, type_: ScErrorType) -> bool {
292 self.as_val().has_minor(type_ as u32)
293 }
294
295 #[inline(always)]
296 pub const fn is_code(&self, code: ScErrorCode) -> bool {
297 self.as_val().has_major(code as u32)
298 }
299
300 #[inline(always)]
301 pub const fn get_code(&self) -> u32 {
302 self.as_val().get_major()
303 }
304
305 #[inline(always)]
306 pub const fn from_contract_error(code: u32) -> Error {
307 unsafe { Self::from_major_minor(code, ScErrorType::Contract as u32) }
308 }
309
310 #[inline(always)]
311 pub const fn from_type_and_code(type_: ScErrorType, code: ScErrorCode) -> Error {
312 unsafe { Self::from_major_minor(code as u32, type_ as u32) }
313 }
314
315 #[inline(always)]
316 pub const fn from_scerror(sc: ScError) -> Error {
317 match sc {
318 ScError::Contract(u) => Self::from_contract_error(u),
319 ScError::WasmVm(code) => Self::from_type_and_code(ScErrorType::WasmVm, code),
320 ScError::Context(code) => Self::from_type_and_code(ScErrorType::Context, code),
321 ScError::Storage(code) => Self::from_type_and_code(ScErrorType::Storage, code),
322 ScError::Object(code) => Self::from_type_and_code(ScErrorType::Object, code),
323 ScError::Crypto(code) => Self::from_type_and_code(ScErrorType::Crypto, code),
324 ScError::Events(code) => Self::from_type_and_code(ScErrorType::Events, code),
325 ScError::Budget(code) => Self::from_type_and_code(ScErrorType::Budget, code),
326 ScError::Value(code) => Self::from_type_and_code(ScErrorType::Value, code),
327 ScError::Auth(code) => Self::from_type_and_code(ScErrorType::Auth, code),
328 }
329 }
330}
331
332impl From<core::convert::Infallible> for crate::Error {
333 fn from(x: core::convert::Infallible) -> Self {
334 match x {}
335 }
336}
337
338#[cfg(all(test, feature = "std"))]
339mod tests {
340 use super::*;
341
342 #[test]
343 fn error_ord_same_as_scerror() {
344 let mut xdr_vals = Vec::new();
352 for type_ in crate::xdr::ScErrorType::VARIANTS {
353 match type_ {
354 ScErrorType::Contract => {
355 for i in 0..=512 {
356 xdr_vals.push(ScError::Contract(i))
357 }
358 }
359 ScErrorType::WasmVm => {
360 for code in crate::xdr::ScErrorCode::VARIANTS {
361 xdr_vals.push(ScError::WasmVm(code))
362 }
363 }
364 ScErrorType::Context => {
365 for code in crate::xdr::ScErrorCode::VARIANTS {
366 xdr_vals.push(ScError::Context(code))
367 }
368 }
369 ScErrorType::Storage => {
370 for code in crate::xdr::ScErrorCode::VARIANTS {
371 xdr_vals.push(ScError::Storage(code))
372 }
373 }
374 ScErrorType::Object => {
375 for code in crate::xdr::ScErrorCode::VARIANTS {
376 xdr_vals.push(ScError::Object(code))
377 }
378 }
379 ScErrorType::Crypto => {
380 for code in crate::xdr::ScErrorCode::VARIANTS {
381 xdr_vals.push(ScError::Crypto(code))
382 }
383 }
384 ScErrorType::Events => {
385 for code in crate::xdr::ScErrorCode::VARIANTS {
386 xdr_vals.push(ScError::Events(code))
387 }
388 }
389 ScErrorType::Budget => {
390 for code in crate::xdr::ScErrorCode::VARIANTS {
391 xdr_vals.push(ScError::Budget(code))
392 }
393 }
394 ScErrorType::Value => {
395 for code in crate::xdr::ScErrorCode::VARIANTS {
396 xdr_vals.push(ScError::Value(code))
397 }
398 }
399 ScErrorType::Auth => {
400 for code in crate::xdr::ScErrorCode::VARIANTS {
401 xdr_vals.push(ScError::Auth(code))
402 }
403 }
404 }
405 }
406
407 let pairs: Vec<_> = xdr_vals
408 .iter()
409 .map(|xdr_val| {
410 let host_val = Error::try_from(xdr_val.clone()).unwrap();
411 (xdr_val, host_val)
412 })
413 .collect();
414
415 let mut pairs_xdr_sorted = pairs.clone();
416 let mut pairs_host_sorted = pairs_xdr_sorted.clone();
417
418 pairs_xdr_sorted.sort_by(|&(v1, _), &(v2, _)| v1.cmp(v2));
419
420 pairs_host_sorted.sort_by(|&(_, v1), &(_, v2)| v1.cmp(&v2));
421
422 assert_eq!(pairs_xdr_sorted, pairs_host_sorted);
423 }
424}