xrpl_wasm_std/core/current_tx/
traits.rs

1//! # Transaction Field Access Traits
2//!
3//! This module defines traits for accessing fields from XRPL transactions in a type-safe manner.
4//! It provides a structured interface for retrieving both common transaction fields (shared across
5//! all transaction types) and transaction-specific fields (unique to particular transaction types).
6//!
7//! ## Overview
8//!
9//! XRPL transactions contain a variety of fields, some mandatory and others optional. This module
10//! organizes field access into logical groups:
11//!
12//! - **Common Fields**: Fields present in all XRPL transactions (Account, Fee, Sequence, etc.)
13//! - **Transaction-Specific Fields**: Fields unique to specific transaction types
14//!
15//! ## Design Philosophy
16//!
17//! The trait-based design provides several benefits:
18//!
19//! - **Type Safety**: Each field is accessed through methods with appropriate return types
20//! - **Composability**: Transaction types can implement multiple traits as needed
21//! - **Zero-Cost Abstraction**: Trait methods compile down to direct host function calls
22//! - **Extensibility**: New transaction types can easily implement the relevant traits
23//!
24//! ## Field Categories
25//!
26//! ### Mandatory vs. Optional Fields
27//!
28//! - **Mandatory fields** return `Result<T>` and will error if missing
29//! - **Optional fields** return `Result<Option<T>>` and return `None` if missing
30//!
31//! ### Field Types
32//!
33//! - **AccountID**: 20-byte account identifiers
34//! - **Hash256**: 256-bit cryptographic hashes
35//! - **Amount**: XRP amounts (with future support for tokens)
36//! - **u32**: 32-bit unsigned integers for sequence numbers, flags, etc.
37//! - **Blob**: Variable-length binary data
38//! - **PublicKey**: 33-byte compressed public keys
39//! - **TransactionType**: Enumerated transaction type identifiers
40
41use crate::core::current_tx::{
42    get_account_id_field, get_amount_field, get_blob_field, get_hash_256_field,
43    get_hash_256_field_optional, get_public_key_field, get_u32_field, get_u32_field_optional,
44};
45use crate::core::types::account_id::AccountID;
46use crate::core::types::amount::token_amount::TokenAmount;
47use crate::core::types::blob::Blob;
48use crate::core::types::crypto_condition::{Condition, Fulfillment};
49use crate::core::types::hash_256::Hash256;
50use crate::core::types::public_key::PublicKey;
51use crate::core::types::transaction_type::TransactionType;
52use crate::host::error_codes::{
53    match_result_code_optional, match_result_code_with_expected_bytes,
54    match_result_code_with_expected_bytes_optional,
55};
56use crate::host::{Result, get_tx_field};
57use crate::sfield;
58
59/// Trait providing access to common fields present in all XRPL transactions.
60///
61/// ## Implementation Requirements
62///
63/// Types implementing this trait should ensure they are used only in the context of a valid
64/// XRPL transaction. The trait methods assume the current transaction context is properly
65/// established by the XRPL Programmability environment.
66pub trait TransactionCommonFields {
67    /// Retrieves the account field from the current transaction.
68    ///
69    /// This field identifies (Required) The unique address of the account that initiated the
70    /// transaction.
71    ///
72    /// # Returns
73    ///
74    /// Returns a `Result<AccountID>` where:
75    /// * `Ok(AccountID)` - The 20-byte account identifier of the transaction sender
76    /// * `Err(Error)` - If the field cannot be retrieved or has an unexpected size
77    fn get_account(&self) -> Result<AccountID> {
78        get_account_id_field(sfield::Account)
79    }
80
81    /// Retrieves the transaction type from the current transaction.
82    ///
83    /// This field specifies the type of transaction. Valid transaction types include:
84    /// Payment, OfferCreate, TrustSet, and many others.
85    ///
86    /// # Returns
87    ///
88    /// Returns a `Result<TransactionType>` where:
89    /// * `Ok(TransactionType)` - An enumerated value representing the transaction type
90    /// * `Err(Error)` - If the field cannot be retrieved or has an unexpected size
91    ///
92    fn get_transaction_type(&self) -> Result<TransactionType> {
93        let mut buffer = [0u8; 2]; // Allocate memory to read into (this is an i32)
94
95        let result_code =
96            unsafe { get_tx_field(sfield::TransactionType, buffer.as_mut_ptr(), buffer.len()) };
97
98        match_result_code_with_expected_bytes(result_code, 2, || i16::from_le_bytes(buffer).into())
99    }
100
101    /// Retrieves the computation allowance from the current transaction.
102    ///
103    /// This field specifies the maximum computational resources that the transaction is
104    /// allowed to consume during execution in the XRPL Programmability environment.
105    /// It helps prevent runaway computations and ensures network stability.
106    ///
107    /// # Returns
108    ///
109    /// Returns a `Result<u32>` where:
110    /// * `Ok(u32)` - The computation allowance value in platform-defined units
111    /// * `Err(Error)` - If the field cannot be retrieved or has an unexpected size
112    fn get_computation_allowance(&self) -> Result<u32> {
113        get_u32_field(sfield::ComputationAllowance)
114    }
115
116    /// Retrieves the fee amount from the current transaction.
117    ///
118    /// This field specifies the amount of XRP (in drops) that the sender is willing to pay
119    /// as a transaction fee. The fee is consumed regardless of whether the transaction
120    /// succeeds or fails, and higher fees can improve transaction priority during
121    /// network congestion.
122    ///
123    /// # Returns
124    ///
125    /// Returns a `Result<Amount>` where:
126    /// * `Ok(Amount)` - The fee amount as an XRP amount in drops
127    /// * `Err(Error)` - If the field cannot be retrieved or has an unexpected size
128    ///
129    /// # Note
130    ///
131    /// Returns XRP amounts only (for now). Future versions may support other token types
132    /// when the underlying amount handling is enhanced.
133    fn get_fee(&self) -> Result<TokenAmount> {
134        get_amount_field(sfield::Fee)
135    }
136
137    /// Retrieves the sequence number from the current transaction.
138    ///
139    /// This field represents the sequence number of the account sending the transaction. A
140    /// transaction is only valid if the Sequence number is exactly 1 greater than the previous
141    /// transaction from the same account. The special case 0 means the transaction is using a
142    /// Ticket instead (Added by the TicketBatch amendment).
143    ///
144    /// # Returns
145    ///
146    /// Returns a `Result<u32>` where:
147    /// * `Ok(u32)` - The transaction sequence number
148    /// * `Err(Error)` - If the field cannot be retrieved or has an unexpected size
149    ///
150    /// # Note
151    ///
152    /// If the transaction uses tickets instead of sequence numbers, this field may not
153    /// be present. In such cases, use `get_ticket_sequence()` instead.
154    fn get_sequence(&self) -> Result<u32> {
155        get_u32_field(sfield::Sequence)
156    }
157
158    /// Retrieves the account transaction ID from the current transaction.
159    ///
160    /// This optional field contains the hash value identifying another transaction. If provided,
161    /// this transaction is only valid if the sending account's previously sent transaction matches
162    /// the provided hash.
163    ///
164    /// # Returns
165    ///
166    /// Returns a `Result<Option<Hash256>>` where:
167    /// * `Ok(Some(Hash256))` - The hash of the required previous transaction
168    /// * `Ok(None)` - If no previous transaction requirement is specified
169    /// * `Err(Error)` - If an error occurred during field retrieval
170    fn get_account_txn_id(&self) -> Result<Option<Hash256>> {
171        get_hash_256_field_optional(sfield::AccountTxnID)
172    }
173
174    /// Retrieves the `flags` field from the current transaction.
175    ///
176    /// This optional field contains a bitfield of transaction-specific flags that modify
177    /// the transaction's behavior.
178    ///
179    /// # Returns
180    ///
181    /// Returns a `Result<Option<u32>>` where:
182    /// * `Ok(Some(u32))` - The flags bitfield if present
183    /// * `Ok(None)` - If no flags are specified (equivalent to flags = 0)
184    /// * `Err(Error)` - If an error occurred during field retrieval
185    fn get_flags(&self) -> Result<Option<u32>> {
186        get_u32_field_optional(sfield::Flags)
187    }
188
189    /// Retrieves the last ledger sequence from the current transaction.
190    ///
191    /// This optional field specifies the highest ledger index this transaction can appear in.
192    /// Specifying this field places a strict upper limit on how long the transaction can wait to
193    /// be validated or rejected. See Reliable Transaction Submission for more details.
194    ///
195    /// # Returns
196    ///
197    /// Returns a `Result<Option<u32>>` where:
198    /// * `Ok(Some(u32))` - The maximum ledger index for transaction inclusion
199    /// * `Ok(None)` - If no expiration is specified (transaction never expires)
200    /// * `Err(Error)` - If an error occurred during field retrieval
201    fn get_last_ledger_sequence(&self) -> Result<Option<u32>> {
202        get_u32_field_optional(sfield::LastLedgerSequence)
203    }
204
205    /// Retrieves the network ID from the current transaction.
206    ///
207    /// This optional field identifies the network ID of the chain this transaction is intended for.
208    /// MUST BE OMITTED for Mainnet and some test networks. REQUIRED on chains whose network ID is
209    /// 1025 or higher.
210    ///
211    /// # Returns
212    ///
213    /// Returns a `Result<Option<u32>>` where:
214    /// * `Ok(Some(u32))` - The network identifier
215    /// * `Ok(None)` - If no specific network is specified (uses default network)
216    /// * `Err(Error)` - If an error occurred during field retrieval
217    fn get_network_id(&self) -> Result<Option<u32>> {
218        get_u32_field_optional(sfield::NetworkID)
219    }
220
221    /// Retrieves the source tag from the current transaction.
222    ///
223    /// This optional field is an arbitrary integer used to identify the reason for this payment, or
224    /// a sender on whose behalf this transaction is made. Conventionally, a refund should specify
225    /// the initial payment's SourceTag as the refund payment's DestinationTag.
226    ///
227    /// # Returns
228    ///
229    /// Returns a `Result<Option<u32>>` where:
230    /// * `Ok(Some(u32))` - The source tag identifier
231    /// * `Ok(None)` - If no source tag is specified
232    /// * `Err(Error)` - If an error occurred during field retrieval
233    fn get_source_tag(&self) -> Result<Option<u32>> {
234        get_u32_field_optional(sfield::SourceTag)
235    }
236
237    /// Retrieves the signing public key from the current transaction.
238    ///
239    /// This field contains the hex representation of the public key that corresponds to the
240    /// private key used to sign this transaction. If an empty string, this field indicates that a
241    /// multi-signature is present in the Signers field instead.
242    ///
243    /// # Returns
244    ///
245    /// Returns a `Result<PublicKey>` where:
246    /// * `Ok(PublicKey)` - The 33-byte compressed public key used for signing
247    /// * `Err(Error)` - If the field cannot be retrieved or has an unexpected size
248    ///
249    /// # Security Note
250    ///
251    /// The presence of this field doesn't guarantee the signature is valid. Instead, this field
252    /// only provides the key claimed to be used for signing. The XRPL network performs signature
253    /// validation before transaction execution.
254    fn get_signing_pub_key(&self) -> Result<PublicKey> {
255        get_public_key_field(sfield::SigningPubKey)
256    }
257
258    /// Retrieves the ticket sequence from the current transaction.
259    ///
260    /// This optional field provides the sequence number of the ticket to use in place of a
261    /// Sequence number. If this is provided, Sequence must be 0. Cannot be used with AccountTxnID.
262    ///
263    /// # Returns
264    ///
265    /// Returns a `Result<Option<u32>>` where:
266    /// * `Ok(Some(u32))` - The ticket sequence number if the transaction uses tickets
267    /// * `Ok(None)` - If the transaction uses traditional sequence numbering
268    /// * `Err(Error)` - If an error occurred during field retrieval
269    ///
270    /// # Note
271    ///
272    /// Transactions use either `Sequence` or `TicketSequence`, but not both. Check this
273    /// field when `get_sequence()` fails or when implementing ticket-aware logic.
274    fn get_ticket_sequence(&self) -> Result<Option<u32>> {
275        get_u32_field_optional(sfield::TicketSequence)
276    }
277
278    /// Retrieves the transaction signature from the current transaction.
279    ///
280    /// This mandatory field contains the signature that verifies this transaction as originating
281    /// from the account it says it is from.
282    ///
283    /// # Returns
284    ///
285    /// Returns a `Result<Blob>` where:
286    /// * `Ok(Blob)` - The transaction signature as variable-length binary data
287    /// * `Err(Error)` - If the field cannot be retrieved
288    ///
289    /// # Security Note
290    ///
291    /// The signature is validated by the XRPL network before transaction execution.
292    /// In the programmability context, you can access the signature for logging or
293    /// analysis purposes, but signature validation has already been performed.
294    fn get_txn_signature(&self) -> Result<Blob> {
295        get_blob_field(sfield::TxnSignature)
296    }
297}
298
299/// Trait providing access to fields specific to EscrowFinish transactions.
300///
301/// This trait extends `TransactionCommonFields` with methods for retrieving fields that are
302/// unique to EscrowFinish transactions. EscrowFinish transactions are used to complete
303/// time-based or condition-based escrows that were previously created with EscrowCreate
304/// transactions.
305///
306/// ## Implementation Requirements
307///
308/// Types implementing this trait should:
309/// - Also implement `TransactionCommonFields` for access to common transaction fields
310/// - Only be used in the context of processing EscrowFinish transactions
311/// - Ensure proper error handling when accessing conditional fields
312pub trait EscrowFinishFields: TransactionCommonFields {
313    /// Retrieves the transaction ID (hash) from the current transaction.
314    ///
315    /// This field provides the unique hash identifier of the current EscrowFinish transaction.
316    /// Transaction hashes are deterministically calculated from the transaction contents
317    /// and serve as unique identifiers for referencing transactions across the XRPL network.
318    ///
319    /// # Returns
320    ///
321    /// Returns a `Result<Hash256>` where:
322    /// * `Ok(Hash256)` - The 256-bit transaction hash identifier
323    /// * `Err(Error)` - If the field cannot be retrieved or has an unexpected size
324    fn get_id(&self) -> Result<Hash256> {
325        get_hash_256_field(sfield::hash)
326    }
327
328    /// Retrieves the owner account from the current EscrowFinish transaction.
329    ///
330    /// This mandatory field identifies the XRPL account that originally created the escrow
331    /// with an EscrowCreate transaction. The owner is the account that deposited the XRP
332    /// into the escrow and specified the conditions for its release.
333    ///
334    /// # Returns
335    ///
336    /// Returns a `Result<AccountID>` where:
337    /// * `Ok(AccountID)` - The 20-byte account identifier of the escrow owner
338    /// * `Err(Error)` - If the field cannot be retrieved or has an unexpected size
339    fn get_owner(&self) -> Result<AccountID> {
340        get_account_id_field(sfield::Owner)
341    }
342
343    /// Retrieves the offer sequence from the current EscrowFinish transaction.
344    ///
345    /// This mandatory field specifies the sequence number of the original EscrowCreate
346    /// transaction that created the escrow being finished. This creates a unique reference
347    /// to the specific escrow object, as escrows are identified by the combination of
348    /// the owner account and the sequence number of the creating transaction.
349    ///
350    /// # Returns
351    ///
352    /// Returns a `Result<u32>` where:
353    /// * `Ok(u32)` - The sequence number of the EscrowCreate transaction
354    /// * `Err(Error)` - If the field cannot be retrieved or has an unexpected size
355    fn get_offer_sequence(&self) -> Result<u32> {
356        get_u32_field(sfield::OfferSequence)
357    }
358
359    /// Retrieves the cryptographic condition from the current EscrowFinish transaction.
360    ///
361    /// This optional field contains the cryptographic condition specified in the
362    /// original EscrowCreate transaction. If present, a valid `Fulfillment` must be provided
363    /// in the `Fulfillment` field for the escrow to be successfully finished. Conditions
364    /// enable complex release criteria beyond simple time-based locks.
365    ///
366    /// # Returns
367    ///
368    /// Returns a `Result<Option<Condition>>` where:
369    /// * `Ok(Some(Condition))` - The 32-byte condition hash if the escrow is conditional
370    /// * `Ok(None)` - If the escrow has no cryptographic condition (time-based only)
371    /// * `Err(Error)` - If an error occurred during field retrieval
372    fn get_condition(&self) -> Result<Option<Condition>> {
373        let mut buffer = [0u8; 32];
374
375        let result_code =
376            unsafe { get_tx_field(sfield::Condition, buffer.as_mut_ptr(), buffer.len()) };
377
378        match_result_code_with_expected_bytes_optional(result_code, 32, || Some(buffer.into()))
379    }
380
381    /// Retrieves the cryptographic fulfillment from the current EscrowFinish transaction.
382    ///
383    /// This optional field contains the cryptographic fulfillment that satisfies the condition
384    /// specified in the original EscrowCreate transaction. The fulfillment must cryptographically
385    /// prove that the condition's requirements have been met. This field is only required
386    /// when the escrow has an associated condition.
387    ///
388    /// # Returns
389    ///
390    /// Returns a `Result<Option<Fulfillment>>` where:
391    /// * `Ok(Some(Fulfillment))` - The fulfillment data if provided
392    /// * `Ok(None)` - If no fulfillment is provided (valid for unconditional escrows)
393    /// * `Err(Error)` - If an error occurred during field retrieval
394    ///
395    /// # Fulfillment Validation
396    ///
397    /// The XRPL network automatically validates that:
398    /// - The fulfillment satisfies the escrow's condition
399    /// - The fulfillment is properly formatted according to RFC 3814
400    /// - The cryptographic proof is mathematically valid
401    ///
402    /// # Size Limits
403    ///
404    /// Fulfillments are limited to 256 bytes in the current XRPL implementation.
405    /// This limit ensures network performance while supporting the most practical
406    /// cryptographic proof scenarios.
407    fn get_fulfillment(&self) -> Result<Option<Fulfillment>> {
408        // Fulfillment fields are limited in rippled to 256 bytes, so we don't use `get_blob_field`
409        // but instead just use a smaller buffer directly.
410
411        let mut buffer = [0u8; 256]; // <-- 256 is the current rippled cap.
412
413        let result_code = unsafe { get_tx_field(sfield::Fulfillment, buffer.as_mut_ptr(), 256) };
414        match_result_code_optional(result_code, || {
415            Some(Fulfillment {
416                data: buffer,
417                len: result_code as usize,
418            })
419        })
420    }
421
422    // TODO: credential IDS
423    // TODO: Signers
424}