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::transaction_type::TransactionType;
66use crate::core::types::uint::{HASH256_SIZE, Hash256};
67use crate::host::error_codes::{
68 match_result_code, match_result_code_optional, match_result_code_with_expected_bytes,
69 match_result_code_with_expected_bytes_optional,
70};
71use crate::host::{Result, get_tx_field};
72
73/// Trait for types that can be retrieved from current transaction fields.
74///
75/// This trait provides a unified interface for retrieving typed data from the current
76/// XRPL transaction being processed, replacing the previous collection of type-specific
77/// functions with a generic, type-safe approach.
78///
79/// ## Supported Types
80///
81/// The following types implement this trait:
82/// - `u32` - 32-bit unsigned integers for sequence numbers, flags, timestamps
83/// - `AccountID` - 20-byte account identifiers for transaction participants
84/// - `Amount` - XRP amounts and token amounts for transaction values
85/// - `Hash256` - 256-bit hashes for transaction IDs and references
86/// - `PublicKey` - 33-byte compressed public keys for cryptographic operations
87/// - `Blob` - Variable-length binary data for signatures, memos, and other content
88///
89/// ## Usage Patterns
90///
91/// ```rust,no_run
92/// use xrpl_wasm_stdlib::core::current_tx::{get_field, get_field_optional};
93/// use xrpl_wasm_stdlib::core::types::account_id::AccountID;
94/// use xrpl_wasm_stdlib::core::types::amount::Amount;
95/// use xrpl_wasm_stdlib::core::types::blob::Blob;
96/// use xrpl_wasm_stdlib::sfield;
97/// # fn example() {
98/// // Get required fields from the current transaction
99/// let account: AccountID = get_field(sfield::Account).unwrap();
100/// let sequence: u32 = get_field(sfield::Sequence).unwrap();
101/// let fee: Amount = get_field(sfield::Fee).unwrap();
102///
103/// // Get optional fields from the current transaction
104/// let flags: Option<u32> = get_field_optional(sfield::Flags).unwrap();
105/// let memo: Option<Blob> = get_field_optional(sfield::Memo).unwrap();
106/// # }
107/// ```
108///
109/// ## Error Handling
110///
111/// - Required field methods return `Result<T>` and error if the field is missing
112/// - Optional field methods return `Result<Option<T>>` and return `None` if the field is missing
113/// - All methods return appropriate errors for buffer size mismatches or other retrieval failures
114///
115/// ## Transaction Context
116///
117/// This trait operates on the "current transaction" - the transaction currently being
118/// processed in the XRPL Programmability environment. The transaction context is
119/// established by the XRPL host environment before calling into WASM code.
120///
121/// ## Safety Considerations
122///
123/// - All implementations use appropriately sized buffers for their data types
124/// - Buffer sizes are validated against expected field sizes where applicable
125/// - Unsafe operations are contained within the host function calls
126/// - Transaction field access is validated by the host environment
127pub trait CurrentTxFieldGetter: Sized {
128 /// Get a required field from the current transaction.
129 ///
130 /// This method retrieves a field that must be present in the transaction.
131 /// If the field is missing, an error is returned.
132 ///
133 /// # Arguments
134 ///
135 /// * `field_code` - The field code identifying which field to retrieve
136 ///
137 /// # Returns
138 ///
139 /// Returns a `Result<Self>` where:
140 /// * `Ok(Self)` - The field value for the specified field
141 /// * `Err(Error::FieldNotFound)` - If the field is not present in the transaction
142 /// * `Err(Error)` - If the field cannot be retrieved or has unexpected size
143 fn get_from_current_tx(field_code: i32) -> Result<Self>;
144
145 /// Get an optional field from the current transaction.
146 ///
147 /// This method retrieves a field that may or may not be present in the transaction.
148 /// If the field is missing, `None` is returned rather than an error.
149 ///
150 /// # Arguments
151 ///
152 /// * `field_code` - The field code identifying which field to retrieve
153 ///
154 /// # Returns
155 ///
156 /// Returns a `Result<Option<Self>>` where:
157 /// * `Ok(Some(Self))` - The field value for the specified field
158 /// * `Ok(None)` - If the field is not present in the transaction
159 /// * `Err(Error)` - If the field cannot be retrieved or has unexpected size
160 fn get_from_current_tx_optional(field_code: i32) -> Result<Option<Self>>;
161}
162
163/// Implementation of `CurrentTxFieldGetter` for 32-bit unsigned integers.
164///
165/// This implementation handles 4-byte integer fields in XRPL transactions.
166/// Common use cases include sequence numbers, flags, timestamps, ledger sequence
167/// numbers, and various counters and identifiers.
168///
169/// # Buffer Management
170///
171/// Uses a 4-byte buffer and validates that exactly 4 bytes are returned
172/// from the host function. The bytes are interpreted as little-endian.
173impl CurrentTxFieldGetter for u32 {
174 fn get_from_current_tx(field_code: i32) -> Result<Self> {
175 let mut buffer = [0u8; 4];
176 let result_code = unsafe { get_tx_field(field_code, buffer.as_mut_ptr(), buffer.len()) };
177 match_result_code_with_expected_bytes(result_code, 4, || u32::from_le_bytes(buffer))
178 }
179
180 fn get_from_current_tx_optional(field_code: i32) -> Result<Option<Self>> {
181 let mut buffer = [0u8; 4];
182 let result_code = unsafe { get_tx_field(field_code, buffer.as_mut_ptr(), buffer.len()) };
183 match_result_code_with_expected_bytes_optional(result_code, 4, || {
184 Some(u32::from_le_bytes(buffer))
185 })
186 }
187}
188
189/// Implementation of `CurrentTxFieldGetter` for XRPL account identifiers.
190///
191/// This implementation handles 20-byte account ID fields in XRPL transactions.
192/// Account IDs identify transaction participants such as the sending account,
193/// destination account, and various other account references throughout the transaction.
194///
195/// # Buffer Management
196///
197/// Uses a 20-byte buffer (ACCOUNT_ID_SIZE) and validates that exactly 20 bytes
198/// are returned from the host function. The buffer is converted to an AccountID
199/// using the `From<[u8; 20]>` implementation.
200impl CurrentTxFieldGetter for AccountID {
201 fn get_from_current_tx(field_code: i32) -> Result<Self> {
202 let mut buffer = [0x00; ACCOUNT_ID_SIZE];
203 let result_code = unsafe { get_tx_field(field_code, buffer.as_mut_ptr(), buffer.len()) };
204 match_result_code_with_expected_bytes(result_code, ACCOUNT_ID_SIZE, || buffer.into())
205 }
206
207 fn get_from_current_tx_optional(field_code: i32) -> Result<Option<Self>> {
208 let mut buffer = [0x00; ACCOUNT_ID_SIZE];
209 let result_code = unsafe { get_tx_field(field_code, buffer.as_mut_ptr(), buffer.len()) };
210 match_result_code_with_expected_bytes_optional(result_code, ACCOUNT_ID_SIZE, || {
211 Some(buffer.into())
212 })
213 }
214}
215
216/// Implementation of `CurrentTxFieldGetter` for XRPL amount values.
217///
218/// This implementation handles amount fields in XRPL transactions, which can represent
219/// either XRP amounts (8 bytes) or token amounts (up to 48 bytes including currency code
220/// and issuer information). Common uses include transaction fees, payment amounts,
221/// offer amounts, and escrow amounts.
222///
223/// # Buffer Management
224///
225/// Uses a 48-byte buffer (AMOUNT_SIZE) to accommodate the largest possible amount
226/// representation. The Amount type handles the parsing of different amount formats
227/// internally. No strict byte count validation is performed since amounts can vary in size.
228impl CurrentTxFieldGetter for Amount {
229 fn get_from_current_tx(field_code: i32) -> Result<Self> {
230 let mut buffer = [0u8; AMOUNT_SIZE];
231 let result_code = unsafe { get_tx_field(field_code, buffer.as_mut_ptr(), buffer.len()) };
232 match_result_code(result_code, || Amount::from(buffer))
233 }
234
235 fn get_from_current_tx_optional(field_code: i32) -> Result<Option<Self>> {
236 let mut buffer = [0u8; AMOUNT_SIZE];
237 let result_code = unsafe { get_tx_field(field_code, buffer.as_mut_ptr(), buffer.len()) };
238 match_result_code_optional(result_code, || Some(Amount::from(buffer)))
239 }
240}
241
242/// Implementation of `CurrentTxFieldGetter` for 256-bit cryptographic hashes.
243///
244/// This implementation handles 32-byte hash fields in XRPL transactions.
245/// Hash256 values are used for transaction IDs, account transaction IDs,
246/// references to other transactions, and various cryptographic identifiers.
247///
248/// # Buffer Management
249///
250/// Uses a 32-byte buffer (HASH256_SIZE) and validates that exactly 32 bytes
251/// are returned from the host function to ensure data integrity.
252impl CurrentTxFieldGetter for Hash256 {
253 fn get_from_current_tx(field_code: i32) -> Result<Self> {
254 let mut buffer = [0u8; HASH256_SIZE];
255 let result_code = unsafe { get_tx_field(field_code, buffer.as_mut_ptr(), buffer.len()) };
256 match_result_code_with_expected_bytes(result_code, HASH256_SIZE, || Hash256::from(buffer))
257 }
258
259 fn get_from_current_tx_optional(field_code: i32) -> Result<Option<Self>> {
260 let mut buffer = [0u8; HASH256_SIZE];
261 let result_code = unsafe { get_tx_field(field_code, buffer.as_mut_ptr(), buffer.len()) };
262 match_result_code_with_expected_bytes_optional(result_code, HASH256_SIZE, || {
263 Some(Hash256::from(buffer))
264 })
265 }
266}
267
268/// Implementation of `CurrentTxFieldGetter` for XRPL public keys.
269///
270/// This implementation handles 33-byte compressed public key fields in XRPL transactions.
271/// Public keys are used for cryptographic signature verification and are commonly found
272/// in the SigningPubKey field and various other cryptographic contexts.
273///
274/// # Buffer Management
275///
276/// Uses a 33-byte buffer and validates that exactly 33 bytes are returned
277/// from the host function. The buffer is converted to a PublicKey using
278/// the `From<[u8; 33]>` implementation.
279impl CurrentTxFieldGetter for PublicKey {
280 fn get_from_current_tx(field_code: i32) -> Result<Self> {
281 let mut buffer = [0u8; 33];
282 let result_code = unsafe { get_tx_field(field_code, buffer.as_mut_ptr(), buffer.len()) };
283 match_result_code_with_expected_bytes(result_code, 33, || buffer.into())
284 }
285
286 fn get_from_current_tx_optional(field_code: i32) -> Result<Option<Self>> {
287 let mut buffer = [0u8; 33];
288 let result_code = unsafe { get_tx_field(field_code, buffer.as_mut_ptr(), buffer.len()) };
289 match_result_code_with_expected_bytes_optional(result_code, 33, || Some(buffer.into()))
290 }
291}
292
293/// Implementation of `CurrentTxFieldGetter` for variable-length binary data.
294///
295/// This implementation handles blob fields in XRPL transactions, which can contain
296/// arbitrary binary data such as transaction signatures, memos, fulfillment data,
297/// and other variable-length content that doesn't fit into fixed-size types.
298///
299/// # Buffer Management
300///
301/// Uses a 1024-byte buffer to accommodate most blob field sizes. The actual
302/// length of the data is determined by the return value from the host function
303/// and stored in the Blob's `len` field. No strict byte count validation is
304/// performed since blobs can vary significantly in size.
305impl CurrentTxFieldGetter for Blob {
306 fn get_from_current_tx(field_code: i32) -> Result<Self> {
307 let mut buffer = [0u8; 1024];
308 let result_code = unsafe { get_tx_field(field_code, buffer.as_mut_ptr(), buffer.len()) };
309 match_result_code(result_code, || Blob {
310 data: buffer,
311 len: result_code as usize,
312 })
313 }
314
315 fn get_from_current_tx_optional(field_code: i32) -> Result<Option<Self>> {
316 let mut buffer = [0u8; 1024];
317 let result_code = unsafe { get_tx_field(field_code, buffer.as_mut_ptr(), buffer.len()) };
318 match_result_code(result_code, || {
319 Some(Blob {
320 data: buffer,
321 len: result_code as usize,
322 })
323 })
324 }
325}
326
327/// Implementation of `CurrentTxFieldGetter` for XRPL TransactionType enums.
328///
329/// This implementation handles 2byte transaction type fields in XRPL transactions.
330///
331/// # Buffer Management
332///
333/// Uses a 2-byte buffer and validates that exactly 2 bytes are returned from the host function.
334impl CurrentTxFieldGetter for TransactionType {
335 fn get_from_current_tx(field_code: i32) -> Result<Self> {
336 let mut buffer = [0u8; 2]; // Allocate memory to read into (this is an i32)
337 let result_code = unsafe { get_tx_field(field_code, buffer.as_mut_ptr(), buffer.len()) };
338 match_result_code_with_expected_bytes(result_code, 2, || i16::from_le_bytes(buffer).into())
339 }
340
341 fn get_from_current_tx_optional(field_code: i32) -> Result<Option<Self>> {
342 let mut buffer = [0u8; 2]; // Allocate memory to read into (this is an i32)
343 let result_code = unsafe { get_tx_field(field_code, buffer.as_mut_ptr(), buffer.len()) };
344 match_result_code_with_expected_bytes_optional(result_code, 2, || Some(buffer.into()))
345 }
346}
347
348/// Retrieves a field from the current transaction.
349///
350/// # Arguments
351///
352/// * `field_code` - The field code identifying which field to retrieve
353///
354/// # Returns
355///
356/// Returns a `Result<T>` where:
357/// * `Ok(T)` - The field value for the specified field
358/// * `Err(Error)` - If the field cannot be retrieved or has unexpected size
359#[inline]
360pub fn get_field<T: CurrentTxFieldGetter>(field_code: i32) -> Result<T> {
361 T::get_from_current_tx(field_code)
362}
363
364/// Retrieves an optionally present field from the current transaction.
365///
366/// # Arguments
367///
368/// * `field_code` - The field code identifying which field to retrieve
369///
370/// # Returns
371///
372/// Returns a `Result<Option<T>>` where:
373/// * `Ok(Some(T))` - The field value for the specified field
374/// * `Ok(None)` - If the field is not present
375/// * `Err(Error)` - If the field cannot be retrieved or has unexpected size
376#[inline]
377pub fn get_field_optional<T: CurrentTxFieldGetter>(field_code: i32) -> Result<Option<T>> {
378 T::get_from_current_tx_optional(field_code)
379}
380
381pub mod escrow_finish;
382pub mod traits;