1use super::fault::*;
2use super::{AuthTokenError, CallingError, HandlingError};
3use crate::ContractTypeSpec;
4use crate::call::CallFault;
5use crate::sys::{SysErr, SyscallErrorCode, SyscallNo};
6use crate::xdr::*;
7use core::error::Error;
8use core::fmt::{self, Display};
9#[cfg(not(target_arch = "riscv32"))]
10use thiserror::Error;
11
12#[derive(Debug, PartialEq, ReadXDR, WriteXDR, ConstXDRSize)]
24#[cfg_attr(not(target_arch = "riscv32"), derive(Error))]
25#[derive(ContractTypeSpec)]
26#[thruput_macros::exn(version = "1.0.0", wire_crate = "sdk")]
30#[repr(i32)]
31#[non_exhaustive]
32pub enum LibraryExn {
33 #[cfg_attr(not(target_arch = "riscv32"), error(transparent))]
34 Syscall(SyscallExn) = 0,
35
36 #[cfg_attr(
37 not(target_arch = "riscv32"),
38 error("Handling a contract call: {0}")
39 )]
40 Handling(HandlingError) = 1,
41
42 #[cfg_attr(
43 not(target_arch = "riscv32"),
44 error("Making a contract call: {0}")
45 )]
46 Calling(CallingError) = 2,
47
48 #[cfg_attr(
49 not(target_arch = "riscv32"),
50 error("Called contract reverted: {0}")
51 )]
52 Called(#[cfg_attr(not(target_arch = "riscv32"), source)] FaultString) = 3,
53
54 #[cfg_attr(
55 not(target_arch = "riscv32"),
56 error("Encoding auth token: {0}")
57 )]
58 AuthToken(AuthTokenError) = 4,
59
60 #[cfg_attr(
61 not(target_arch = "riscv32"),
62 error("bytestring_get_sized: expected {expected}, found {found}")
63 )]
64 BytestringWrongSize { expected: u32, found: u32 } = 5,
65
66 #[cfg_attr(
67 not(target_arch = "riscv32"),
68 error("non-negative int went negative")
69 )]
70 NonNegativeIntUnderflow = 6,
71
72 #[cfg_attr(
73 not(target_arch = "riscv32"),
74 error("non-negative int overflow")
75 )]
76 NonNegativeIntOverflow = 7,
77
78 #[cfg_attr(
79 not(target_arch = "riscv32"),
80 error("storage value failed to (de)serialize")
81 )]
82 StorageSerialization = 8,
83
84 #[cfg_attr(
85 not(target_arch = "riscv32"),
86 error("storage index out of bounds: len={len}, access={access}")
87 )]
88 StorageIndexOutOfBounds { len: u64, access: u64 } = 9,
89
90 #[cfg_attr(
91 not(target_arch = "riscv32"),
92 error("storage keypath component is too long to encode")
93 )]
94 StorageKeypathOverflow = 10,
95
96 #[cfg_attr(
97 not(target_arch = "riscv32"),
98 error(
99 "retrocausal queue entry: expected {expected} bytes, \
100 found {found}"
101 )
102 )]
103 RetrocausalQueueEntryWrongSize { expected: u32, found: u32 } = 11,
104
105 #[cfg_attr(
106 not(target_arch = "riscv32"),
107 error(
108 "capacity constraint: set_capacity conflicts with a prior \
109 write this transaction"
110 )
111 )]
112 CapacityConstraintAlreadyWritten = 12,
113
114 #[cfg_attr(
122 not(target_arch = "riscv32"),
123 error("no entrypoint on this contract matches the invoked method")
124 )]
125 UnknownMethod = 13,
126}
127
128impl From<HandlingError> for LibraryExn {
129 fn from(e: HandlingError) -> Self {
130 Self::Handling(e)
131 }
132}
133
134impl From<CallingError> for LibraryExn {
135 fn from(e: CallingError) -> Self {
136 Self::Calling(e)
137 }
138}
139
140impl From<AuthTokenError> for LibraryExn {
141 fn from(e: AuthTokenError) -> Self {
142 Self::AuthToken(e)
143 }
144}
145
146impl crate::re_throw::ReThrow for LibraryExn {
149 #[cfg_attr(not(target_arch = "riscv32"), track_caller)]
150 fn re_throw(self) -> ! {
151 library_throw(self)
152 }
153}
154
155impl LibraryExn {
156 pub fn as_syscall_exn(&self) -> Option<&SyscallExn> {
157 if let LibraryExn::Syscall(se) = self {
158 Some(se)
159 } else {
160 None
161 }
162 }
163
164 pub fn is_storage_missing(&self) -> bool {
175 match self.as_syscall_exn() {
176 Some(se) => {
177 se.has_code(
178 SyscallErrorCode::kSyscallErrorCodeStorageKeyNotExist,
179 ) || se.has_code(
180 SyscallErrorCode::kSyscallErrorCodeNoRetrocausalQueueEntry,
181 )
182 }
183 None => false,
184 }
185 }
186}
187
188pub trait NoneIfMissing<T> {
191 fn none_if_missing(self) -> Option<T>;
194}
195
196impl<T> NoneIfMissing<T> for Result<T, LibraryExn> {
197 #[cfg_attr(not(target_arch = "riscv32"), track_caller)]
198 fn none_if_missing(self) -> Option<T> {
199 use crate::re_throw::ReThrow;
200 match self {
201 Ok(v) => Some(v),
202 Err(e) if e.is_storage_missing() => None,
203 Err(e) => e.re_throw(),
204 }
205 }
206}
207
208impl crate::__contract_spec::ContractTypeName for LibraryExn {
209 const NAME: crate::__contract_spec::ContractSpecIdView<'static> =
210 crate::__contract_spec::ContractSpecIdView {
211 crate_name: ::core::env!("CARGO_PKG_NAME"),
212 major: crate::exn::EXN_ABI_MAJOR,
213 module_path: ::core::module_path!(),
214 declared: "LibraryExn",
215 };
216}
217
218#[cfg_attr(not(target_arch = "riscv32"), track_caller)]
219pub fn library_throw<E: Into<LibraryExn> + core::fmt::Debug>(e: E) -> ! {
220 super::abort_encode::library_abort(&e.into())
221}
222
223#[cfg_attr(not(target_arch = "riscv32"), track_caller)]
224pub fn client_library_throw<E: Into<LibraryExn> + core::fmt::Debug>(
225 e: E,
226) -> ! {
227 super::abort_encode::client_library_abort(&e.into())
228}
229
230#[cfg_attr(not(target_arch = "riscv32"), track_caller)]
233pub fn syscall_throw(callno: SyscallNo, errno: SysErr) -> ! {
234 library_throw(syscall_exn(callno, errno))
235}
236
237pub fn syscall_exn(callno: SyscallNo, errno: SysErr) -> LibraryExn {
238 LibraryExn::Syscall(SyscallExn::new(callno, errno))
239}
240
241#[cfg_attr(not(target_arch = "riscv32"), track_caller)]
242pub fn nnint_overflow() -> ! {
243 library_throw(LibraryExn::NonNegativeIntOverflow)
244}
245
246#[cfg_attr(not(target_arch = "riscv32"), track_caller)]
247pub fn nnint_underflow() -> ! {
248 library_throw(LibraryExn::NonNegativeIntUnderflow)
249}
250
251#[cfg_attr(not(target_arch = "riscv32"), track_caller)]
252pub fn decoding_args_throw(e: &CallFault) -> ! {
253 library_throw(HandlingError::DecodingArgs(FaultString::from_display(e)))
254}
255
256#[cfg_attr(not(target_arch = "riscv32"), track_caller)]
257pub fn encoding_success_throw<E: Error>(e: E) -> ! {
258 library_throw(HandlingError::EncodingSuccess(FaultString::from_display(e)))
259}
260
261#[cfg_attr(not(target_arch = "riscv32"), track_caller)]
262pub fn encoding_exception_throw<E: Error>(e: E) -> ! {
263 library_throw(HandlingError::EncodingException(FaultString::from_display(
264 e,
265 )))
266}
267
268#[cfg_attr(not(target_arch = "riscv32"), track_caller)]
269pub fn encoding_event_throw<E: Display>(e: E) -> ! {
270 library_throw(HandlingError::EncodingEvent(FaultString::from_display(e)))
271}
272
273#[derive(Debug, Clone, Copy, ReadXDR, WriteXDR, PartialEq, ConstXDRSize)]
274#[cfg_attr(not(target_arch = "riscv32"), derive(Error))]
275#[derive(ContractTypeSpec)]
276pub struct SyscallExn {
277 callno: SyscallNo,
278 errno: SysErr,
279}
280
281impl SyscallExn {
282 pub fn new(callno: SyscallNo, errno: SysErr) -> Self {
283 Self { callno, errno }
284 }
285
286 pub fn callno(&self) -> SyscallNo {
287 self.callno
288 }
289
290 pub fn has_code(&self, err_code: SyscallErrorCode) -> bool {
291 self.errno.is_code(err_code)
292 }
293
294 pub fn is(&self, call_no: SyscallNo, err_code: SyscallErrorCode) -> bool {
295 self.callno == call_no && self.errno == err_code.into()
296 }
297}
298
299impl Display for SyscallExn {
300 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
301 write!(f, "Syscall {:?} failed with {:?}", self.callno, self.errno)
302 }
303}
304
305impl crate::__contract_spec::ContractTypeName for SyscallExn {
306 const NAME: crate::__contract_spec::ContractSpecIdView<'static> =
307 crate::__contract_spec::ContractSpecIdView {
308 crate_name: ::core::env!("CARGO_PKG_NAME"),
309 major: crate::exn::EXN_ABI_MAJOR,
310 module_path: ::core::module_path!(),
311 declared: "SyscallExn",
312 };
313}
314
315#[cfg(test)]
316mod selector_tests {
317 use super::*;
318 use crate::exn::Exn;
319
320 #[test]
321 fn library_exn_selector_matches_sha256() {
322 let expected = thruput_contract_util::exception_selector(
325 "1.0.0",
326 "sdk",
327 "LibraryExn",
328 );
329 assert_eq!(LibraryExn::SELECTOR, expected);
330 }
331
332 #[test]
333 fn library_exn_selector_nonzero() {
334 assert_ne!(LibraryExn::SELECTOR, 0);
335 }
336}