xrpl_std/host/mod.rs
1use crate::core::error_codes;
2
3mod host_bindings;
4mod host_bindings_for_testing;
5pub mod trace;
6//////////////////////////////////////
7// Host functions (defined by the host)
8//////////////////////////////////////
9
10#[cfg(not(target_arch = "wasm32"))]
11include!("host_bindings_for_testing.rs");
12
13// host functions defined by the host.
14#[cfg(target_arch = "wasm32")]
15include!("host_bindings.rs");
16
17/// `Result` is a type that represents either success ([`Ok`]) or failure ([`Err`]) that better
18/// conforms to the xrpld programmability APIs.
19#[must_use]
20pub enum Result<T> {
21 /// Contains the success value
22 Ok(T),
23 /// Contains the error value
24 Err(Error), // TODO: Test if the WASM size is expanded if we use an enum here instead of i32
25}
26
27impl<T> Result<T> {
28 /// Returns `true` if the result is [`Ok`].
29 #[inline]
30 pub fn is_ok(&self) -> bool {
31 matches!(*self, Result::Ok(_))
32 }
33
34 /// Returns `true` if the result is [`Err`].
35 #[inline]
36 pub fn is_err(&self) -> bool {
37 !self.is_ok()
38 }
39
40 /// Converts from `Result<T>` to `Option<T>`.
41 ///
42 /// Converts `self` into an `Option<T>`, consuming `self`,
43 /// and discarding the error, if any.
44 #[inline]
45 pub fn ok(self) -> Option<T> {
46 match self {
47 Result::Ok(x) => Some(x),
48 Result::Err(_) => None,
49 }
50 }
51
52 /// Converts from `Result<T>` to `Option<Error>`.
53 ///
54 /// Converts `self` into an `Option<Error>`, consuming `self`,
55 /// and discarding the success value, if any.
56 #[inline]
57 pub fn err(self) -> Option<Error> {
58 match self {
59 Result::Ok(_) => None,
60 Result::Err(x) => Some(x),
61 }
62 }
63
64 /// Returns the contained [`Ok`] value, consuming the `self` value.
65 ///
66 /// # Panics
67 ///
68 /// Panics if the value is an [`Err`], with a panic message provided by the
69 /// [`Err`]'s value.
70 #[inline]
71 pub fn unwrap(self) -> T {
72 match self {
73 Result::Ok(t) => t,
74 Result::Err(error) => {
75 let _ = trace::trace_num("error_code=", error.code() as i64);
76 panic!(
77 "called `Result::unwrap()` on an `Err` with code: {}",
78 error.code()
79 )
80 }
81 }
82 }
83
84 /// Returns the contained [`Ok`] value or a provided default.
85 #[inline]
86 pub fn unwrap_or(self, default: T) -> T {
87 match self {
88 Result::Ok(t) => t,
89 Result::Err(_) => default,
90 }
91 }
92
93 /// Returns the contained [`Ok`] value or computes it from a closure.
94 #[inline]
95 pub fn unwrap_or_else<F: FnOnce(Error) -> T>(self, op: F) -> T {
96 match self {
97 Result::Ok(t) => t,
98 Result::Err(e) => op(e),
99 }
100 }
101
102 #[inline]
103 pub fn unwrap_or_panic(self) -> T {
104 self.unwrap_or_else(|error| {
105 let _ = trace::trace_num("error_code=", error.code() as i64);
106 core::panic!(
107 "Failed in {}: error_code={}",
108 core::panic::Location::caller(),
109 error.code()
110 );
111 })
112 }
113}
114
115impl From<i64> for Result<u64> {
116 #[inline(always)] // <-- Inline because this function is very small
117 fn from(value: i64) -> Self {
118 match value {
119 res if res >= 0 => Result::Ok(value as _),
120 _ => Result::Err(Error::from_code(value as _)),
121 }
122 }
123}
124
125/// Possible errors returned by XRPL Programmability APIs.
126///
127/// Errors are global across all Programmability APIs.
128#[derive(Clone, Copy, Debug)]
129#[repr(i32)]
130pub enum Error {
131 /// A pointer or buffer length provided as a parameter described memory outside the allowed memory region.
132 OutOfBounds = error_codes::POINTER_OUT_OF_BOUNDS,
133 /// Reserved for internal invariant trips, generally unrelated to inputs.
134 /// These should be reported with an issue.
135 InternalError = error_codes::INTERNAL_ERROR,
136 // TODO: Remove Option and check for this error for any optional fields.
137 FieldNotFound = error_codes::FIELD_NOT_FOUND,
138 NoFreeSlots = error_codes::NO_FREE_SLOTS,
139 // /// Attempted to set a parameter or value larger than the allowed space .
140 // TooBig = _c::TOO_BIG,
141 // /// The API was unable to produce output to the write_ptr because the specified write_len was too small
142 // TooSmall = _c::TOO_SMALL,
143 // /// The requested object or item wasn't found
144 // DoesntExist = _c::DOESNT_EXIST,
145 // /// The Hook attempted to allocate an item into a slot, but there were no slots free.
146 // /// To avoid ensure re-use of existing slots. The maximum number of slots is 255.
147 // NoFreeSlots = _c::NO_FREE_SLOTS,
148 // /// One or more of the parameters to the API were invalid according to the individual API's specification.
149 // InvalidArgument = _c::INVALID_ARGUMENT,
150 // /// Some APIs allow for a once-per-execution parameter to be set.
151 // /// A second attempt to set a once-per-execution parameter results in this error.
152 // AlreadySet = _c::ALREADY_SET,
153 // /// An API required the Hook to do something before the API is allowed to be called.
154 // /// Check the API's documentation.
155 // PrerequisiteNotMet = _c::PREREQUISITE_NOT_MET,
156 // /// During fee calculation if an absurdly large fee is calculated this error is returned.
157 // FeeTooLarge = _c::FEE_TOO_LARGE,
158 // /// An attempt to emit() a TXN was unsccessful for any of a number of reasons.
159 // /// Check the trace log of the rippled to which you are submitting the originating TXN.
160 // EmissionFailure = _c::EMISSION_FAILURE,
161 // /// A Hook may only use up to 256 calls to nonce() per execution.
162 // /// Further calls result in this error code.
163 // TooManyNonces = _c::TOO_MANY_NONCES,
164 // /// A Hook must declare ahead of time how many TXN it intends to emit().
165 // /// If it emits fewer than this many, this is allowed.
166 // /// If it emits more than this many this error is returned.
167 // TooManyEmittedTxn = _c::TOO_MANY_EMITTED_TXN,
168 // /// While Hooks is/was in development an API may return this if some or all of that API is planned but not yet implemented.
169 // NotImplemented = _c::NOT_IMPLEMENTED,
170 // /// An API which accepts a 20 byte Account ID may return this if, in its opinion, the Account ID was not valid for any reason.
171 // InvalidAccount = _c::INVALID_ACCOUNT,
172 // /// All loops inside a Hook must declare at the top of the loop, as the first non trivial instruction,
173 // /// before any branch instruction, the promised maximum number of iterations of the loop.
174 // /// If this promise is violated the hook terminates immediately with this error code.
175 // GuardViolation = _c::GUARD_VIOLATION,
176 // The requested serialized field could not be found in the specified object.
177 // InvalidField = _c::INVALID_FIELD,
178 // /// While parsing serialized content an error was encountered (typically indicating an invalidly serialized object).
179 // ParseError = _c::PARSE_ERROR,
180 // /// Used internally to communicate a rollback event.
181 // RcRollback = _c::RC_ROLLBACK,
182 // /// Used internally to communicate an accept event.
183 // RcAccept = _c::RC_ACCEPT,
184 // /// Specified keylet could not be found, or keylet is invalid
185 // NoSuchKeylet = _c::NO_SUCH_KEYLET,
186 // /// API was asked to assume object under analysis is an STArray but it was not.
187 // NotAnArray = -22,
188 // /// API was asked to assume object under analysis is an STObject but it was not.
189 // NotAnObject = -23,
190 // /// A floating point operation resulted in Not-A-Number or API call attempted to specify an XFL floating point number outside of the expressible range of XFL.
191 // InvalidFloat = _c::INVALID_FLOAT,
192 // /// API call would result in a division by zero, so API ended early.
193 // DivisionByZero = -25,
194 // /// When attempting to create an XFL the mantissa must be 16 decimal digits.
195 // ManitssaOversized = -26,
196 // /// When attempting to create an XFL the mantissa must be 16 decimal digits.
197 // MantissaUndersized = -27,
198 // /// When attempting to create an XFL the exponent must not exceed 80.
199 // ExponentOversized = -28,
200 // /// When attempting to create an XFL the exponent must not be less than -96.
201 // ExponentUndersized = -29,
202 // /// A floating point operation done on an XFL resulted in a value larger than XFL format is able to represent.
203 // Overflow = -30,
204 /// An API assumed an STAmount was an IOU when in fact it was XRP or MPT.
205 NotIouAmount = -31,
206 /// An API assumed an STAmount was an IOU when in fact it was XRP or MPT.
207 NotMptAmount = -32,
208 /// An API assumed an STAmount was an IOU when in fact it was XRP or MPT.
209 NotXrpAmount = -33,
210 /// An API assumed an STObject was an STAmount when in fact it was not.
211 NotAnAmount = -34,
212 // /// An API would have returned a negative integer except that negative integers are reserved for error codes (i.e. what you are reading.)
213 // CantReturnNegative = -33,
214}
215
216impl Error {
217 // TODO: Use Trait instead?
218 #[inline(always)] // <-- Inline because this function is very small
219 pub fn from_code(code: i32) -> Self {
220 unsafe { core::mem::transmute(code) }
221 }
222
223 /// Error code
224 #[inline(always)] // <-- Inline because this function is very small
225 pub fn code(self) -> i32 {
226 self as _
227 }
228}
229
230impl From<Error> for i64 {
231 fn from(val: Error) -> Self {
232 val as i64
233 }
234}
235
236/// Converts a `Result<Option<T>>` to a `Result<T>` by treating `None` as an error.
237///
238/// This utility function is commonly used in the XRPL Programmability API context
239/// where operations may return optional values that should be treated as errors
240/// when absent.
241///
242/// # Arguments
243///
244/// * `result` - A `Result` containing an `Option<T>` that needs to be unwrapped
245///
246/// # Returns
247///
248/// * `Result::Ok(value)` - If the input was `Result::Ok(Some(value))`
249/// * `Result::Err(Error::FieldNotFound)` - If the input was `Result::Ok(None)`
250/// * `Result::Err(err)` - If the input was `Result::Err(err)`, the error is propagated
251///
252/// # Error Handling
253///
254/// When the optional value is `None`, this function returns `Error::FieldNotFound`,
255/// which is appropriate for cases where a required field or value is missing from
256/// XRPL ledger objects or API responses.
257pub(crate) fn to_non_optional<T>(result: Result<Option<T>>) -> Result<T> {
258 match result {
259 Result::Ok(Some(value)) => Result::Ok(value),
260 Result::Ok(None) => Result::Err(Error::FieldNotFound),
261 Result::Err(err) => Result::Err(err),
262 }
263}
264pub const FLOAT_ONE: [u8; 8] = [0xD4, 0x83, 0x8D, 0x7E, 0xA4, 0xC6, 0x80, 0x00];
265pub const FLOAT_NEGATIVE_ONE: [u8; 8] = [0x94, 0x83, 0x8D, 0x7E, 0xA4, 0xC6, 0x80, 0x00];