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