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];