xrpl_wasm_stdlib/core/current_tx/
mod.rs

1//! # Current Transaction Retrieval Module
2//!
3//! This module provides utilities for retrieving typed fields from the current XRPL transaction
4//! within the context of XRPL Programmability. It offers a safe, type-safe
5//! interface over the low-level host functions for accessing transaction data, such as from an
6//! `EscrowFinish` transaction.
7//!
8//! ## Overview
9//!
10//! When processing XRPL transactions in a permissionless programmability environment, you often
11//! need to extract specific fields like account IDs, hashes, public keys, and other data. This
12//! module provides convenient wrapper functions that handle the low-level buffer management
13//! and error handling required to safely retrieve these fields.
14//!
15//! ## Field Types Supported
16//!
17//! - **AccountID**: 20-byte account identifiers
18//! - **u32**: 32-bit unsigned integers
19//! - **Hash256**: 256-bit cryptographic hashes
20//! - **PublicKey**: 33-byte public keys
21//! - **Blob**: Variable-length binary data
22//!
23//! ## Optional vs Required Fields
24//!
25//! The module provides both optional and required variants for field retrieval:
26//!
27//! - **Required variants** (e.g., `get_u32_field`): Return an error if the field is missing
28//! - **Optional variants** (e.g., `get_optional_u32_field`): Return `None` if the field is missing
29//!
30//! ## Error Handling
31//!
32//! All functions return `Result<T>` or `Result<Option<T>>` types that encapsulate
33//! the custom error handling required for the XRPL Programmability environment.
34//!
35//! ## Safety Considerations
36//!
37//! - All functions use fixed-size buffers appropriate for their data types
38//! - Buffer sizes are validated against expected field sizes
39//! - Unsafe operations are contained within the low-level host function calls
40//! - Memory safety is ensured through proper buffer management
41//! - Field codes are validated by the underlying host functions
42//!
43//! ## Performance Notes
44//!
45//! - All functions are marked `#[inline]` to minimize call overhead
46//! - Buffer allocations are stack-based and have minimal cost
47//! - Host function calls are the primary performance bottleneck
48//!
49//! ## Example
50//!
51//! Get sender Account and optional flags:
52//!
53//! ```no_run
54//! use xrpl_wasm_stdlib::core::current_tx::escrow_finish::EscrowFinish;
55//! use xrpl_wasm_stdlib::core::current_tx::traits::TransactionCommonFields;
56//! let tx = EscrowFinish;
57//! let account = tx.get_account().unwrap_or_panic();
58//! let _flags = tx.get_flags().unwrap_or_panic();
59//! ```
60
61use crate::core::types::account_id::{ACCOUNT_ID_SIZE, AccountID};
62use crate::core::types::amount::{AMOUNT_SIZE, Amount};
63use crate::core::types::blob::Blob;
64use crate::core::types::public_key::PublicKey;
65use crate::core::types::signature::{SIGNATURE_MAX_SIZE, Signature};
66use crate::core::types::transaction_type::TransactionType;
67use crate::core::types::uint::{HASH256_SIZE, Hash256};
68use crate::host::error_codes::{match_result_code, match_result_code_optional};
69use crate::host::field_helpers::{
70    get_fixed_size_field_with_expected_bytes, get_fixed_size_field_with_expected_bytes_optional,
71    get_variable_size_field, get_variable_size_field_optional,
72};
73use crate::host::{Result, get_tx_field};
74
75/// Trait for types that can be retrieved from current transaction fields.
76///
77/// This trait provides a unified interface for retrieving typed data from the current
78/// XRPL transaction being processed, replacing the previous collection of type-specific
79/// functions with a generic, type-safe approach.
80///
81/// ## Supported Types
82///
83/// The following types implement this trait:
84/// - `u32` - 32-bit unsigned integers for sequence numbers, flags, timestamps
85/// - `AccountID` - 20-byte account identifiers for transaction participants
86/// - `Amount` - XRP amounts and token amounts for transaction values
87/// - `Hash256` - 256-bit hashes for transaction IDs and references
88/// - `PublicKey` - 33-byte compressed public keys for cryptographic operations
89/// - `Blob<N>` - Variable-length binary data (generic over buffer size `N`)
90///
91/// ## Usage Patterns
92///
93/// ```rust,no_run
94/// use xrpl_wasm_stdlib::core::current_tx::{get_field, get_field_optional};
95/// use xrpl_wasm_stdlib::core::types::account_id::AccountID;
96/// use xrpl_wasm_stdlib::core::types::amount::Amount;
97/// use xrpl_wasm_stdlib::core::types::blob::{Blob, MEMO_BLOB_SIZE};
98/// use xrpl_wasm_stdlib::sfield;
99/// # fn example() {
100///   // Get required fields from the current transaction
101///   let account: AccountID = get_field(sfield::Account).unwrap();
102///   let sequence: u32 = get_field(sfield::Sequence).unwrap();
103///   let fee: Amount = get_field(sfield::Fee).unwrap();
104///
105///   // Get optional fields from the current transaction
106///   let flags: Option<u32> = get_field_optional(sfield::Flags).unwrap();
107///   let memo: Option<Blob<{MEMO_BLOB_SIZE}>> = get_field_optional(sfield::Memo).unwrap();
108/// # }
109/// ```
110///
111/// ## Error Handling
112///
113/// - Required field methods return `Result<T>` and error if the field is missing
114/// - Optional field methods return `Result<Option<T>>` and return `None` if the field is missing
115/// - All methods return appropriate errors for buffer size mismatches or other retrieval failures
116///
117/// ## Transaction Context
118///
119/// This trait operates on the "current transaction" - the transaction currently being
120/// processed in the XRPL Programmability environment. The transaction context is
121/// established by the XRPL host environment before calling into WASM code.
122///
123/// ## Safety Considerations
124///
125/// - All implementations use appropriately sized buffers for their data types
126/// - Buffer sizes are validated against expected field sizes where applicable
127/// - Unsafe operations are contained within the host function calls
128/// - Transaction field access is validated by the host environment
129pub trait CurrentTxFieldGetter: Sized {
130    /// Get a required field from the current transaction.
131    ///
132    /// This method retrieves a field that must be present in the transaction.
133    /// If the field is missing, an error is returned.
134    ///
135    /// # Arguments
136    ///
137    /// * `field_code` - The field code identifying which field to retrieve
138    ///
139    /// # Returns
140    ///
141    /// Returns a `Result<Self>` where:
142    /// * `Ok(Self)` - The field value for the specified field
143    /// * `Err(Error::FieldNotFound)` - If the field is not present in the transaction
144    /// * `Err(Error)` - If the field cannot be retrieved or has unexpected size
145    fn get_from_current_tx(field_code: i32) -> Result<Self>;
146
147    /// Get an optional field from the current transaction.
148    ///
149    /// This method retrieves a field that may or may not be present in the transaction.
150    /// If the field is missing, `None` is returned rather than an error.
151    ///
152    /// # Arguments
153    ///
154    /// * `field_code` - The field code identifying which field to retrieve
155    ///
156    /// # Returns
157    ///
158    /// Returns a `Result<Option<Self>>` where:
159    /// * `Ok(Some(Self))` - The field value for the specified field
160    /// * `Ok(None)` - If the field is not present in the transaction
161    /// * `Err(Error)` - If the field cannot be retrieved or has unexpected size
162    fn get_from_current_tx_optional(field_code: i32) -> Result<Option<Self>>;
163}
164
165/// Implementation of `CurrentTxFieldGetter` for 32-bit unsigned integers.
166///
167/// This implementation handles 4-byte integer fields in XRPL transactions.
168/// Common use cases include sequence numbers, flags, timestamps, ledger sequence
169/// numbers, and various counters and identifiers.
170///
171/// # Buffer Management
172///
173/// Uses a 4-byte buffer and validates that exactly 4 bytes are returned
174/// from the host function. The bytes are interpreted as little-endian.
175impl CurrentTxFieldGetter for u32 {
176    #[inline]
177    fn get_from_current_tx(field_code: i32) -> Result<Self> {
178        match get_fixed_size_field_with_expected_bytes::<4, _>(field_code, |fc, buf, size| unsafe {
179            get_tx_field(fc, buf, size)
180        }) {
181            Result::Ok(buffer) => Result::Ok(u32::from_le_bytes(buffer)),
182            Result::Err(e) => Result::Err(e),
183        }
184    }
185
186    #[inline]
187    fn get_from_current_tx_optional(field_code: i32) -> Result<Option<Self>> {
188        match get_fixed_size_field_with_expected_bytes_optional::<4, _>(
189            field_code,
190            |fc, buf, size| unsafe { get_tx_field(fc, buf, size) },
191        ) {
192            Result::Ok(buffer) => Result::Ok(buffer.map(u32::from_le_bytes)),
193            Result::Err(e) => Result::Err(e),
194        }
195    }
196}
197
198/// Implementation of `CurrentTxFieldGetter` for XRPL account identifiers.
199///
200/// This implementation handles 20-byte account ID fields in XRPL transactions.
201/// Account IDs identify transaction participants such as the sending account,
202/// destination account, and various other account references throughout the transaction.
203///
204/// # Buffer Management
205///
206/// Uses a 20-byte buffer (ACCOUNT_ID_SIZE) and validates that exactly 20 bytes
207/// are returned from the host function. The buffer is converted to an AccountID
208/// using the `From<[u8; 20]>` implementation.
209impl CurrentTxFieldGetter for AccountID {
210    #[inline]
211    fn get_from_current_tx(field_code: i32) -> Result<Self> {
212        match get_fixed_size_field_with_expected_bytes::<ACCOUNT_ID_SIZE, _>(
213            field_code,
214            |fc, buf, size| unsafe { get_tx_field(fc, buf, size) },
215        ) {
216            Result::Ok(buffer) => Result::Ok(buffer.into()),
217            Result::Err(e) => Result::Err(e),
218        }
219    }
220
221    #[inline]
222    fn get_from_current_tx_optional(field_code: i32) -> Result<Option<Self>> {
223        match get_fixed_size_field_with_expected_bytes_optional::<ACCOUNT_ID_SIZE, _>(
224            field_code,
225            |fc, buf, size| unsafe { get_tx_field(fc, buf, size) },
226        ) {
227            Result::Ok(buffer) => Result::Ok(buffer.map(|b| b.into())),
228            Result::Err(e) => Result::Err(e),
229        }
230    }
231}
232
233/// Implementation of `CurrentTxFieldGetter` for XRPL amount values.
234///
235/// This implementation handles amount fields in XRPL transactions, which can represent
236/// either XRP amounts (8 bytes) or token amounts (up to 48 bytes including currency code
237/// and issuer information). Common uses include transaction fees, payment amounts,
238/// offer amounts, and escrow amounts.
239///
240/// # Buffer Management
241///
242/// Uses a 48-byte buffer (AMOUNT_SIZE) to accommodate the largest possible amount
243/// representation. The Amount type handles the parsing of different amount formats
244/// internally. No strict byte count validation is performed since amounts can vary in size.
245impl CurrentTxFieldGetter for Amount {
246    #[inline]
247    fn get_from_current_tx(field_code: i32) -> Result<Self> {
248        match get_variable_size_field::<AMOUNT_SIZE, _>(field_code, |fc, buf, size| unsafe {
249            get_tx_field(fc, buf, size)
250        }) {
251            Result::Ok((buffer, _len)) => Result::Ok(Amount::from(buffer)),
252            Result::Err(e) => Result::Err(e),
253        }
254    }
255
256    #[inline]
257    fn get_from_current_tx_optional(field_code: i32) -> Result<Option<Self>> {
258        match get_variable_size_field_optional::<AMOUNT_SIZE, _>(
259            field_code,
260            |fc, buf, size| unsafe { get_tx_field(fc, buf, size) },
261        ) {
262            Result::Ok(opt) => Result::Ok(opt.map(|(buffer, _len)| Amount::from(buffer))),
263            Result::Err(e) => Result::Err(e),
264        }
265    }
266}
267
268/// Implementation of `CurrentTxFieldGetter` for 256-bit cryptographic hashes.
269///
270/// This implementation handles 32-byte hash fields in XRPL transactions.
271/// Hash256 values are used for transaction IDs, account transaction IDs,
272/// references to other transactions, and various cryptographic identifiers.
273///
274/// # Buffer Management
275///
276/// Uses a 32-byte buffer (HASH256_SIZE) and validates that exactly 32 bytes
277/// are returned from the host function to ensure data integrity.
278impl CurrentTxFieldGetter for Hash256 {
279    #[inline]
280    fn get_from_current_tx(field_code: i32) -> Result<Self> {
281        match get_fixed_size_field_with_expected_bytes::<HASH256_SIZE, _>(
282            field_code,
283            |fc, buf, size| unsafe { get_tx_field(fc, buf, size) },
284        ) {
285            Result::Ok(buffer) => Result::Ok(buffer.into()),
286            Result::Err(e) => Result::Err(e),
287        }
288    }
289
290    #[inline]
291    fn get_from_current_tx_optional(field_code: i32) -> Result<Option<Self>> {
292        match get_fixed_size_field_with_expected_bytes_optional::<HASH256_SIZE, _>(
293            field_code,
294            |fc, buf, size| unsafe { get_tx_field(fc, buf, size) },
295        ) {
296            Result::Ok(buffer) => Result::Ok(buffer.map(|b| b.into())),
297            Result::Err(e) => Result::Err(e),
298        }
299    }
300}
301
302/// Implementation of `CurrentTxFieldGetter` for XRPL public keys.
303///
304/// This implementation handles 33-byte compressed public key fields in XRPL transactions.
305/// Public keys are used for cryptographic signature verification and are commonly found
306/// in the SigningPubKey field and various other cryptographic contexts.
307///
308/// # Buffer Management
309///
310/// Uses a 33-byte buffer and validates that exactly 33 bytes are returned
311/// from the host function. The buffer is converted to a PublicKey using
312/// the `From<[u8; 33]>` implementation.
313impl CurrentTxFieldGetter for PublicKey {
314    #[inline]
315    fn get_from_current_tx(field_code: i32) -> Result<Self> {
316        match get_fixed_size_field_with_expected_bytes::<33, _>(
317            field_code,
318            |fc, buf, size| unsafe { get_tx_field(fc, buf, size) },
319        ) {
320            Result::Ok(buffer) => Result::Ok(buffer.into()),
321            Result::Err(e) => Result::Err(e),
322        }
323    }
324
325    #[inline]
326    fn get_from_current_tx_optional(field_code: i32) -> Result<Option<Self>> {
327        match get_fixed_size_field_with_expected_bytes_optional::<33, _>(
328            field_code,
329            |fc, buf, size| unsafe { get_tx_field(fc, buf, size) },
330        ) {
331            Result::Ok(buffer) => Result::Ok(buffer.map(|b| b.into())),
332            Result::Err(e) => Result::Err(e),
333        }
334    }
335}
336
337/// Implementation of `CurrentTxFieldGetter` for XRPL transaction signatures.
338///
339/// This implementation handles signature fields in XRPL transactions, which can contain
340/// either EdDSA (64 bytes) or ECDSA (70-72 bytes) signatures. The buffer is sized to
341/// accommodate the maximum possible signature size (72 bytes).
342///
343/// # Buffer Management
344///
345/// Uses a 72-byte buffer to accommodate both signature types. The actual length of the
346/// signature is determined by the return value from the host function and stored in the
347/// Signature's underlying Blob `len` field.
348impl CurrentTxFieldGetter for Signature {
349    #[inline]
350    fn get_from_current_tx(field_code: i32) -> Result<Self> {
351        let mut buffer = core::mem::MaybeUninit::<[u8; SIGNATURE_MAX_SIZE]>::uninit();
352        let result_code =
353            unsafe { get_tx_field(field_code, buffer.as_mut_ptr().cast(), SIGNATURE_MAX_SIZE) };
354        match_result_code(result_code, || {
355            Signature(Blob {
356                data: unsafe { buffer.assume_init() },
357                len: result_code as usize,
358            })
359        })
360    }
361
362    #[inline]
363    fn get_from_current_tx_optional(field_code: i32) -> Result<Option<Self>> {
364        let mut buffer = core::mem::MaybeUninit::<[u8; SIGNATURE_MAX_SIZE]>::uninit();
365        let result_code =
366            unsafe { get_tx_field(field_code, buffer.as_mut_ptr().cast(), SIGNATURE_MAX_SIZE) };
367        match_result_code_optional(result_code, || {
368            Some(Signature(Blob {
369                data: unsafe { buffer.assume_init() },
370                len: result_code as usize,
371            }))
372        })
373    }
374}
375
376/// Implementation of `CurrentTxFieldGetter` for variable-length binary data.
377///
378/// This implementation handles blob fields in XRPL transactions, which can contain
379/// arbitrary binary data such as transaction signatures, memos, fulfillment data,
380/// and other variable-length content that doesn't fit into fixed-size types.
381///
382/// # Buffer Management
383///
384/// Uses a buffer of size `N` to accommodate blob field data. The actual
385/// length of the data is determined by the return value from the host function
386/// and stored in the Blob's `len` field. No strict byte count validation is
387/// performed since blobs can vary significantly in size.
388///
389/// # Type Parameters
390///
391/// * `N` - The maximum capacity of the blob buffer in bytes
392impl<const N: usize> CurrentTxFieldGetter for Blob<N> {
393    #[inline]
394    fn get_from_current_tx(field_code: i32) -> Result<Self> {
395        match get_variable_size_field::<N, _>(field_code, |fc, buf, size| unsafe {
396            get_tx_field(fc, buf, size)
397        }) {
398            Result::Ok((data, len)) => Result::Ok(Blob { data, len }),
399            Result::Err(e) => Result::Err(e),
400        }
401    }
402
403    #[inline]
404    fn get_from_current_tx_optional(field_code: i32) -> Result<Option<Self>> {
405        match get_variable_size_field_optional::<N, _>(field_code, |fc, buf, size| unsafe {
406            get_tx_field(fc, buf, size)
407        }) {
408            Result::Ok(opt) => Result::Ok(opt.map(|(data, len)| Blob { data, len })),
409            Result::Err(e) => Result::Err(e),
410        }
411    }
412}
413
414/// Implementation of `CurrentTxFieldGetter` for XRPL TransactionType enums.
415///
416/// This implementation handles 2byte transaction type fields in XRPL transactions.
417///
418/// # Buffer Management
419///
420/// Uses a 2-byte buffer and validates that exactly 2 bytes are returned from the host function.
421impl CurrentTxFieldGetter for TransactionType {
422    #[inline]
423    fn get_from_current_tx(field_code: i32) -> Result<Self> {
424        match get_fixed_size_field_with_expected_bytes::<2, _>(field_code, |fc, buf, size| unsafe {
425            get_tx_field(fc, buf, size)
426        }) {
427            Result::Ok(buffer) => Result::Ok(i16::from_le_bytes(buffer).into()),
428            Result::Err(e) => Result::Err(e),
429        }
430    }
431
432    #[inline]
433    fn get_from_current_tx_optional(field_code: i32) -> Result<Option<Self>> {
434        match get_fixed_size_field_with_expected_bytes_optional::<2, _>(
435            field_code,
436            |fc, buf, size| unsafe { get_tx_field(fc, buf, size) },
437        ) {
438            Result::Ok(buffer) => Result::Ok(buffer.map(|b| i16::from_le_bytes(b).into())),
439            Result::Err(e) => Result::Err(e),
440        }
441    }
442}
443
444/// Retrieves a field from the current transaction.
445///
446/// # Arguments
447///
448/// * `field_code` - The field code identifying which field to retrieve
449///
450/// # Returns
451///
452/// Returns a `Result<T>` where:
453/// * `Ok(T)` - The field value for the specified field
454/// * `Err(Error)` - If the field cannot be retrieved or has unexpected size
455#[inline]
456pub fn get_field<T: CurrentTxFieldGetter>(field_code: i32) -> Result<T> {
457    T::get_from_current_tx(field_code)
458}
459
460/// Retrieves an optionally present field from the current transaction.
461///
462/// # Arguments
463///
464/// * `field_code` - The field code identifying which field to retrieve
465///
466/// # Returns
467///
468/// Returns a `Result<Option<T>>` where:
469/// * `Ok(Some(T))` - The field value for the specified field
470/// * `Ok(None)` - If the field is not present
471/// * `Err(Error)` - If the field cannot be retrieved or has unexpected size
472#[inline]
473pub fn get_field_optional<T: CurrentTxFieldGetter>(field_code: i32) -> Result<Option<T>> {
474    T::get_from_current_tx_optional(field_code)
475}
476
477pub mod escrow_finish;
478pub mod traits;