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}