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;