xrpl_wasm_std/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_std::core::current_tx::escrow_finish::EscrowFinish;
55//! use xrpl_wasm_std::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::token_amount::{TOKEN_AMOUNT_SIZE, TokenAmount};
63use crate::core::types::blob::Blob;
64use crate::core::types::hash_256::{HASH256_SIZE, Hash256};
65use crate::core::types::public_key::PublicKey;
66use crate::host::error_codes::{
67    match_result_code, match_result_code_with_expected_bytes,
68    match_result_code_with_expected_bytes_optional,
69};
70use crate::host::{Result, get_tx_field, to_non_optional};
71
72pub mod escrow_finish;
73pub mod traits;
74
75/// Retrieves an AccountID field from the current transaction.
76///
77/// This function extracts a 20-byte account identifier from the current XRPL transaction.
78/// Account IDs are used to identify XRPL accounts in various transaction fields such as
79/// the transaction sender (`Account`), destination (`Destination`), or other account references.
80///
81/// # Arguments
82///
83/// * `field_code` - The field code identifying which AccountID field to retrieve. This corresponds
84///   to the XRPL transaction field identifier.
85///
86/// # Returns
87///
88/// Returns a `Result<AccountID>` where:
89/// * `Ok(AccountID)` - The 20-byte account identifier for the specified field
90/// * `Err(Error)` - If the field cannot be retrieved, is missing (for required fields), or has an
91///   unexpected size
92///
93/// # Errors
94///
95/// This function returns an error if:
96/// - The specified field is not present in the transaction
97/// - The field data is not exactly 20 bytes (ACCOUNT_ID_SIZE)
98/// - The underlying host function call fails
99#[inline]
100fn get_account_id_field(field_code: i32) -> Result<AccountID> {
101    let mut buffer = [0x00; ACCOUNT_ID_SIZE];
102
103    let result_code = unsafe { get_tx_field(field_code, buffer.as_mut_ptr(), buffer.len()) };
104
105    match_result_code_with_expected_bytes(result_code, ACCOUNT_ID_SIZE, || buffer.into())
106}
107
108/// Retrieves a `TokenAmount` field from the current ledger object.
109///
110/// # Arguments
111///
112/// * `field_code` - The field code identifying which field to retrieve
113///
114/// # Returns
115///
116/// Returns a `Result<TokenAmount>` where:
117/// * `Ok(AccountID)` - The account identifier for the specified field
118/// * `Err(Error)` - If the field cannot be retrieved or has an unexpected size.
119#[inline]
120fn get_amount_field(field_code: i32) -> Result<TokenAmount> {
121    let mut buffer = [0u8; TOKEN_AMOUNT_SIZE]; // Enough to hold an Amount
122
123    let result_code = unsafe { get_tx_field(field_code, buffer.as_mut_ptr(), buffer.len()) };
124
125    match_result_code(result_code, || TokenAmount::from(buffer))
126}
127
128/// Retrieves a `u32` field from the current transaction.
129///
130/// This function extracts a 32-bit unsigned integer from the current XRPL transaction.
131/// u32 fields are commonly used for sequence numbers, flags, timestamps, and other
132/// numeric values in XRPL transactions.
133///
134/// # Arguments
135///
136/// * `field_code` - The field code identifying which u32 field to retrieve. This corresponds to
137///   the XRPL transaction field identifier.
138///
139/// # Returns
140///
141/// Returns a `Result<u32>` where:
142/// * `Ok(u32)` - The 32-bit unsigned integer value for the specified field
143/// * `Err(Error)` - If the field cannot be retrieved, is missing, or has an unexpected size
144///
145/// # Errors
146///
147/// This function returns an error if:
148/// - The specified field is not present in the transaction
149/// - The field data is not exactly 4 bytes
150/// - The underlying host function call fails
151///
152/// # See Also
153///
154/// * [`get_u32_field_optional`] - For optional u32 fields that may not be present
155#[inline]
156fn get_u32_field(field_code: i32) -> Result<u32> {
157    to_non_optional(get_u32_field_optional(field_code))
158}
159
160/// Retrieves an optional `u32` field from the current transaction.
161///
162/// This function extracts a 32-bit unsigned integer from the current XRPL transaction,
163/// returning `None` if the field is not present. This is useful for optional transaction
164/// fields that may or may not be included in the transaction data.
165///
166/// # Arguments
167///
168/// * `field_code` - The field code identifying which u32 field to retrieve. This corresponds to
169///   the XRPL transaction field identifier.
170///
171/// # Returns
172///
173/// Returns a `Result<Option<u32>>` where:
174/// * `Ok(Some(u32))` - The 32-bit unsigned integer value if the field is present
175/// * `Ok(None)` - If the field is not present in the transaction (this is not an error)
176/// * `Err(Error)` - If an error occurred during field retrieval or the field has unexpected size
177///
178/// # Errors
179///
180/// This function returns an error if:
181/// - The field data is present but not exactly 4 bytes
182/// - The underlying host function call fails for reasons other than missing field
183///
184/// # See Also
185///
186/// * [`get_u32_field`] - For required u32 fields that must be present
187#[inline]
188fn get_u32_field_optional(field_code: i32) -> Result<Option<u32>> {
189    let mut buffer = [0u8; 4]; // Enough to hold an u32
190
191    let result_code = unsafe { get_tx_field(field_code, buffer.as_mut_ptr(), buffer.len()) };
192
193    match_result_code_with_expected_bytes_optional(result_code, 4, || {
194        Some(u32::from_le_bytes(buffer)) // <-- Move the buffer into a u32
195    })
196}
197
198/// Retrieves a `Hash256` field from the current transaction.
199///
200/// This function extracts a 256-bit cryptographic hash from the current XRPL transaction.
201/// Hash256 fields are used for transaction hashes, previous transaction references,
202/// ledger hashes, and other cryptographic identifiers in XRPL.
203///
204/// # Arguments
205///
206/// * `field_code` - The field code identifying which Hash256 field to retrieve. This corresponds
207///   to the XRPL transaction field identifier.
208///
209/// # Returns
210///
211/// Returns a `Result<Hash256>` where:
212/// * `Ok(Hash256)` - The 256-bit hash for the specified field
213/// * `Err(Error)` - If the field cannot be retrieved, is missing, or has an unexpected size
214///
215/// # Errors
216///
217/// This function returns an error if:
218/// - The specified field is not present in the transaction
219/// - The field data is not exactly 32 bytes (HASH256_SIZE)
220/// - The underlying host function call fails
221///
222/// # See Also
223///
224/// * [`get_hash_256_field_optional`] - For optional Hash256 fields that may not be present
225#[inline]
226fn get_hash_256_field(field_code: i32) -> Result<Hash256> {
227    to_non_optional(get_hash_256_field_optional(field_code))
228}
229
230/// Retrieves an optional `Hash256` field from the current transaction.
231///
232/// This function extracts a 256-bit cryptographic hash from the current XRPL transaction,
233/// returning `None` if the field is not present. This is useful for optional hash fields
234/// that may or may not be included in the transaction data.
235///
236/// # Arguments
237///
238/// * `field_code` - The field code identifying which Hash256 field to retrieve. This corresponds
239///   to the XRPL transaction field identifier.
240///
241/// # Returns
242///
243/// Returns a `Result<Option<Hash256>>` where:
244/// * `Ok(Some(Hash256))` - The 256-bit hash if the field is present
245/// * `Ok(None)` - If the field is not present in the transaction (this is not an error)
246/// * `Err(Error)` - If an error occurred during field retrieval or the field has unexpected size
247///
248/// # Errors
249///
250/// This function returns an error if:
251/// - The field data is present but not exactly 32 bytes (HASH256_SIZE)
252/// - The underlying host function call fails for reasons other than missing field
253///
254/// # See Also
255///
256/// * [`get_hash_256_field`] - For required Hash256 fields that must be present
257#[inline]
258fn get_hash_256_field_optional(field_code: i32) -> Result<Option<Hash256>> {
259    let mut buffer = [0u8; HASH256_SIZE]; // Enough to hold 256 bits (32 bytes)
260
261    let result_code = unsafe { get_tx_field(field_code, buffer.as_mut_ptr(), buffer.len()) };
262
263    match_result_code_with_expected_bytes(result_code, HASH256_SIZE, || {
264        Some(Hash256(buffer)) // <-- Move the buffer into an Hash256
265    })
266}
267
268/// Retrieves a `PublicKey` field from the current transaction.
269///
270/// This function extracts a 33-byte compressed public key from the current XRPL transaction.
271/// Public key fields are used for cryptographic operations, signature verification,
272/// and account authentication in XRPL transactions.
273///
274/// # Arguments
275///
276/// * `field_code` - The field code identifying which PublicKey field to retrieve. This corresponds
277///   to the XRPL transaction field identifier.
278///
279/// # Returns
280///
281/// Returns a `Result<PublicKey>` where:
282/// * `Ok(PublicKey)` - The 33-byte compressed public key for the specified field
283/// * `Err(Error)` - If the field cannot be retrieved, is missing, or has an unexpected size
284///
285/// # Errors
286///
287/// This function returns an error if:
288/// - The specified field is not present in the transaction
289/// - The field data is not exactly 33 bytes (compressed public key size)
290/// - The underlying host function call fails
291///
292/// # See Also
293///
294/// * [`get_optional_public_key_field`] - For optional PublicKey fields that may not be present
295#[inline]
296fn get_public_key_field(field_code: i32) -> Result<PublicKey> {
297    to_non_optional(get_optional_public_key_field(field_code))
298}
299
300/// Retrieves an optional `PublicKey` field from the current transaction.
301///
302/// This function extracts a 33-byte compressed public key from the current XRPL transaction,
303/// returning `None` if the field is not present. This is useful for optional public key fields
304/// that may or may not be included in the transaction data.
305///
306/// # Arguments
307///
308/// * `field_code` - The field code identifying which PublicKey field to retrieve. This corresponds
309///   to the XRPL transaction field identifier.
310///
311/// # Returns
312///
313/// Returns a `Result<Option<PublicKey>>` where:
314/// * `Ok(Some(PublicKey))` - The 33-byte compressed public key if the field is present
315/// * `Ok(None)` - If the field is not present in the transaction (this is not an error)
316/// * `Err(Error)` - If an error occurred during field retrieval or the field has unexpected size
317///
318/// # Errors
319///
320/// This function returns an error if:
321/// - The field data is present but not exactly 33 bytes
322/// - The underlying host function call fails for reasons other than missing field
323///
324/// # See Also
325///
326/// * [`get_public_key_field`] - For required PublicKey fields that must be present
327#[inline]
328fn get_optional_public_key_field(field_code: i32) -> Result<Option<PublicKey>> {
329    let mut buffer = [0u8; 33];
330
331    let result_code = unsafe { get_tx_field(field_code, buffer.as_mut_ptr(), buffer.len()) };
332
333    match_result_code_with_expected_bytes_optional(result_code, 33, || Some(buffer.into()))
334}
335
336/// Retrieves a variable-length `Blob` field from the current transaction.
337///
338/// This function extracts variable-length binary data from the current XRPL transaction.
339/// Blob fields are used for memos, arbitrary data, encoded objects, and other variable-length
340/// information that doesn't fit into fixed-size field types.
341///
342/// # Arguments
343///
344/// * `field_code` - The field code identifying which blob field to retrieve. This corresponds to
345///   the XRPL transaction field identifier.
346///
347/// # Returns
348///
349/// Returns a `Result<Blob>` where:
350/// * `Ok(Blob)` - The blob data with its actual length encoded in the structure
351/// * `Err(Error)` - If the field cannot be retrieved or is missing
352///
353/// # Buffer Management
354///
355/// This function uses a 1024-byte buffer to accommodate the largest possible field,
356/// which is typically a memo field. The actual length of the retrieved data is
357/// encoded in the returned `Blob` structure, allowing for efficient handling of
358/// variable-length data without unnecessary allocations.
359///
360/// # Errors
361///
362/// This function returns an error if:
363/// - The specified field is not present in the transaction
364/// - The underlying host function call fails
365/// - The field data exceeds 1024 bytes (buffer overflow protection)
366///
367/// # See Also
368///
369/// * [`get_blob_field_optional`] - For optional blob fields that may not be present
370#[inline]
371fn get_blob_field(field_code: i32) -> Result<Blob> {
372    to_non_optional(get_blob_field_optional(field_code))
373}
374
375/// Retrieves an optional variable-length `Blob` field from the current transaction.
376///
377/// This function extracts variable-length binary data from the current XRPL transaction,
378/// returning `None` if the field is not present. This is useful for optional blob fields
379/// such as memos or custom data that may or may not be included in the transaction.
380///
381/// # Arguments
382///
383/// * `field_code` - The field code identifying which blob field to retrieve. This corresponds to
384///   the XRPL transaction field identifier.
385///
386/// # Returns
387///
388/// Returns a `Result<Option<Blob>>` where:
389/// * `Ok(Some(Blob))` - The blob data with its actual length if the field is present
390/// * `Ok(None)` - If the field is not present in the transaction (this is not an error)
391/// * `Err(Error)` - If an error occurred during field retrieval
392///
393/// # Buffer Management
394///
395/// This function uses a 1024-byte buffer to accommodate the largest possible field,
396/// which is typically a memo field. The actual length of the retrieved data is
397/// encoded in the returned `Blob` structure. Only the bytes up to the actual length
398/// contain valid data; the rest of the buffer should be ignored.
399///
400/// # Errors
401///
402/// This function will return an error if:
403/// - The underlying host function call fails for reasons other than missing field
404/// - The field data would exceed 1024 bytes (buffer overflow protection)
405///
406/// # Performance Notes
407///
408/// - The 1024-byte buffer is allocated on the stack for performance
409/// - Only the actual data length (stored in `result_code`) is meaningful
410/// - The buffer size is chosen to handle the largest expected XRPL field
411///
412/// # See Also
413///
414/// * [`get_blob_field`] - For required blob fields that must be present
415/// * [`Blob`] - The structure used to represent variable-length binary data
416#[inline]
417fn get_blob_field_optional(field_code: i32) -> Result<Option<Blob>> {
418    let mut buffer = [0u8; 1024]; // Enough to hold the largest field, which is a memo.
419
420    let result_code = unsafe { get_tx_field(field_code, buffer.as_mut_ptr(), buffer.len()) };
421
422    match_result_code(result_code, || {
423        Some(Blob {
424            data: buffer,
425            len: result_code as usize,
426        })
427    })
428}