xrpl_std/core/ledger_objects/
traits.rs

1/// This module provides traits for interacting with XRP Ledger objects.
2///
3/// It defines common interfaces for accessing and manipulating different types of ledger objects,
4/// particularly focusing on Escrow objects. The traits provide methods to get and set various
5/// fields of ledger objects, with separate traits for current ledger objects and general ledger objects.
6use crate::core::error_codes::{
7    match_result_code, match_result_code_with_expected_bytes,
8    match_result_code_with_expected_bytes_optional,
9};
10use crate::core::ledger_objects::{current_ledger_object, ledger_object};
11use crate::core::types::account_id::AccountID;
12use crate::core::types::amount::token_amount::TokenAmount;
13use crate::core::types::blob::Blob;
14use crate::core::types::contract_data::{ContractData, XRPL_CONTRACT_DATA_SIZE};
15use crate::core::types::crypto_condition::Condition;
16use crate::core::types::hash_256::Hash256;
17use crate::host::{Error, get_current_ledger_obj_field, get_ledger_obj_field, update_data};
18use crate::host::{Result, Result::Err, Result::Ok};
19use crate::sfield;
20
21/// Trait providing access to common fields present in all ledger objects.
22///
23/// This trait defines methods to access standard fields that are common across
24/// different types of ledger objects in the XRP Ledger.
25pub trait LedgerObjectCommonFields {
26    /// Retrieves the ledger index (unique identifier) of the ledger object.
27    ///
28    /// # Arguments
29    ///
30    /// * `register_num` - The register number where the ledger object is stored
31    ///
32    /// # Returns
33    ///
34    /// The ledger index as a Hash256 value
35    fn get_ledger_index(&self, register_num: i32) -> Result<Hash256> {
36        ledger_object::get_hash_256_field(register_num, sfield::LedgerIndex)
37    }
38
39    /// Retrieves the flags field of the ledger object.
40    ///
41    /// # Arguments
42    ///
43    /// * `register_num` - The register number where the ledger object is stored
44    ///
45    /// # Returns
46    ///
47    /// The flags as a u32 value
48    fn get_flags(&self, register_num: i32) -> Result<u32> {
49        ledger_object::get_u32_field(register_num, sfield::Flags)
50    }
51
52    /// Retrieves the ledger entry type of the object.
53    ///
54    /// The value 0x0075, mapped to the string Escrow, indicates that this is an Escrow entry.
55    ///
56    /// # Returns
57    ///
58    /// The ledger entry type as a u16 value
59    fn get_ledger_entry_type(&self) -> Result<u16> {
60        current_ledger_object::get_u16_field(sfield::LedgerEntryType)
61    }
62}
63
64/// Trait providing access to common fields in the current ledger object.
65///
66/// This trait defines methods to access standard fields that are common across
67/// different types of ledger objects, specifically for the current ledger object
68/// being processed.
69pub trait CurrentLedgerObjectCommonFields {
70    /// Retrieves the ledger index (unique identifier) of the current ledger object.
71    ///
72    /// # Returns
73    ///
74    /// The ledger index as a Hash256 value
75    fn get_ledger_index(&self) -> Result<Hash256> {
76        current_ledger_object::get_hash_256_field(sfield::LedgerIndex)
77    }
78
79    /// Retrieves the flags field of the current ledger object.
80    ///
81    /// # Returns
82    ///
83    /// The flags as a u32 value
84    fn get_get_flags(&self) -> Result<u32> {
85        current_ledger_object::get_u32_field(sfield::Flags)
86    }
87
88    /// Retrieves the ledger entry type of the current ledger object.
89    ///
90    /// The value 0x0075, mapped to the string Escrow, indicates that this is an Escrow entry.
91    ///
92    /// # Returns
93    ///
94    /// The ledger entry type as a u16 value
95    fn get_ledger_entry_type(&self) -> Result<u16> {
96        current_ledger_object::get_u16_field(sfield::LedgerEntryType)
97    }
98}
99
100/// Trait providing access to fields specific to Escrow objects in the current ledger.
101///
102/// This trait extends `CurrentLedgerObjectCommonFields` and provides methods to access
103/// fields that are specific to Escrow objects in the current ledger being processed.
104pub trait CurrentEscrowFields: CurrentLedgerObjectCommonFields {
105    /// The address of the owner (sender) of this escrow. This is the account that provided the XRP
106    /// and gets it back if the escrow is canceled.
107    fn get_account(&self) -> Result<AccountID> {
108        current_ledger_object::get_account_id_field(sfield::Account)
109    }
110
111    /// The amount currently held in the escrow (could be XRP, IOU, or MPT).
112    fn get_amount(&self) -> Result<TokenAmount> {
113        current_ledger_object::get_amount_field(sfield::Amount)
114    }
115
116    /// The escrow can be canceled if and only if this field is present and the time it specifies
117    /// has passed. Specifically, this is specified as seconds since the Ripple Epoch and it
118    /// "has passed" if it's earlier than the close time of the previous validated ledger.
119    fn get_cancel_after(&self) -> Result<Option<u32>> {
120        current_ledger_object::get_u32_field_optional(sfield::CancelAfter)
121    }
122
123    /// A PREIMAGE-SHA-256 crypto-condition, as hexadecimal. If present, the EscrowFinish
124    /// transaction must contain a fulfillment that satisfies this condition.
125    fn get_condition(&self) -> Result<Option<Condition>> {
126        let mut buffer = [0u8; 32];
127
128        let result_code = unsafe {
129            get_current_ledger_obj_field(sfield::Condition, buffer.as_mut_ptr(), buffer.len())
130        };
131
132        match_result_code_with_expected_bytes_optional(result_code, 32, || Some(buffer.into()))
133    }
134
135    /// The destination address where the XRP is paid if the escrow is successful.
136    fn get_destination(&self) -> Result<AccountID> {
137        current_ledger_object::get_account_id_field(sfield::Destination)
138    }
139
140    /// A hint indicating which page of the destination's owner directory links to this object, in
141    /// case the directory consists of multiple pages. Omitted on escrows created before enabling the fix1523 amendment.
142    fn get_destination_node(&self) -> Result<Option<u64>> {
143        current_ledger_object::get_u64_field_optional(sfield::DestinationNode)
144    }
145
146    /// An arbitrary tag to further specify the destination for this escrow, such as a hosted
147    /// recipient at the destination address.
148    fn get_destination_tag(&self) -> Result<Option<u32>> {
149        current_ledger_object::get_u32_field_optional(sfield::DestinationTag)
150    }
151
152    /// The time, in seconds since the Ripple Epoch, after which this escrow can be finished. Any
153    /// EscrowFinish transaction before this time fails. (Specifically, this is compared with the
154    /// close time of the previous validated ledger.)
155    fn get_finish_after(&self) -> Result<Option<u32>> {
156        current_ledger_object::get_u32_field_optional(sfield::FinishAfter)
157    }
158
159    // TODO: Implement this function.
160    // /// The value 0x0075, mapped to the string Escrow, indicates that this is an Escrow entry.
161    // fn get_ledger_entry_type(&self) -> Result<LedgerEntryType> {
162    //     return Ok(LedgerEntryType::Escrow);
163    // }
164
165    /// A hint indicating which page of the sender's owner directory links to this entry, in case
166    /// the directory consists of multiple pages.
167    fn get_owner_node(&self) -> Result<u64> {
168        current_ledger_object::get_u64_field(sfield::OwnerNode)
169    }
170
171    /// The identifying hash of the transaction that most recently modified this entry.
172    fn get_previous_txn_id(&self) -> Result<Hash256> {
173        current_ledger_object::get_hash_256_field(sfield::PreviousTxnID)
174    }
175
176    /// The index of the ledger that contains the transaction that most recently modified this
177    /// entry.
178    fn get_previous_txn_lgr_seq(&self) -> Result<u32> {
179        current_ledger_object::get_u32_field(sfield::PreviousTxnLgrSeq)
180    }
181
182    /// An arbitrary tag to further specify the source for this escrow, such as a hosted recipient
183    /// at the owner's address.
184    fn get_source_tag(&self) -> Result<Option<u32>> {
185        current_ledger_object::get_u32_field_optional(sfield::SourceTag)
186    }
187
188    /// The WASM code that is executing.
189    fn get_finish_function(&self) -> Result<Option<Blob>> {
190        current_ledger_object::get_blob_field_optional(sfield::FinishFunction)
191    }
192
193    /// Retrieves the contract `data` from the current escrow object.
194    ///
195    /// This function fetches the `data` field from the current ledger object and returns it as a
196    /// ContractData structure. The data is read into a fixed-size buffer of XRPL_CONTRACT_DATA_SIZE.
197    ///
198    /// # Returns
199    ///
200    /// Returns a `Result<ContractData>` where:
201    /// * `Ok(ContractData)` - Contains the retrieved data and its actual length
202    /// * `Err(Error)` - If the retrieval operation failed
203    fn get_data(&self) -> Result<ContractData> {
204        let mut data: [u8; XRPL_CONTRACT_DATA_SIZE] = [0; XRPL_CONTRACT_DATA_SIZE];
205
206        let result_code =
207            unsafe { get_current_ledger_obj_field(sfield::Data, data.as_mut_ptr(), data.len()) };
208
209        match result_code {
210            code if code >= 0 => Ok(ContractData {
211                data,
212                len: code as usize,
213            }),
214            code => Err(Error::from_code(code)),
215        }
216    }
217
218    /// Updates the contract data in the current escrow object.
219    ///
220    /// # Arguments
221    ///
222    /// * `data` - The contract data to update
223    ///
224    /// # Returns
225    ///
226    /// Returns a `Result<()>` where:
227    /// * `Ok(())` - The data was successfully updated
228    /// * `Err(Error)` - If the update operation failed
229    fn update_current_escrow_data(data: ContractData) -> Result<()> {
230        // TODO: Make sure rippled always deletes any existing data bytes in rippled, and sets the new
231        // length to be `data.len` (e.g., if the developer writes 2 bytes, then that's the new
232        // length and any old bytes are lost).
233        let result_code = unsafe { update_data(data.data.as_ptr(), data.len) };
234        match_result_code_with_expected_bytes(result_code, data.len, || ())
235    }
236}
237
238/// Trait providing access to fields specific to Escrow objects in any ledger.
239///
240/// This trait extends `LedgerObjectCommonFields` and provides methods to access
241/// fields that are specific to Escrow objects in any ledger, not just the current one.
242/// Each method requires a register number to identify which ledger object to access.
243pub trait EscrowFields: LedgerObjectCommonFields {
244    /// The address of the owner (sender) of this escrow. This is the account that provided the XRP
245    /// and gets it back if the escrow is canceled.
246    fn get_account(&self, register_num: i32) -> Result<AccountID> {
247        ledger_object::get_account_id_field(register_num, sfield::Account)
248    }
249
250    /// The amount of XRP, in drops, currently held in the escrow.
251    fn get_amount(&self, register_num: i32) -> Result<TokenAmount> {
252        // Create a buffer large enough for any TokenAmount type
253        const BUFFER_SIZE: usize = 48usize;
254        let mut buffer = [0u8; BUFFER_SIZE];
255
256        let result_code = unsafe {
257            get_ledger_obj_field(
258                register_num,
259                sfield::Amount,
260                buffer.as_mut_ptr(),
261                buffer.len(),
262            )
263        };
264
265        match_result_code(result_code, || TokenAmount::from(buffer))
266    }
267
268    /// The escrow can be canceled if and only if this field is present and the time it specifies
269    /// has passed. Specifically, this is specified as seconds since the Ripple Epoch and it
270    /// "has passed" if it's earlier than the close time of the previous validated ledger.
271    fn get_cancel_after(&self, register_num: i32) -> Result<Option<u32>> {
272        ledger_object::get_u32_field_optional(register_num, sfield::CancelAfter)
273    }
274
275    /// A PREIMAGE-SHA-256 crypto-condition, as hexadecimal. If present, the EscrowFinish
276    /// transaction must contain a fulfillment that satisfies this condition.
277    fn get_condition(&self, register_num: i32) -> Result<Option<Condition>> {
278        let mut buffer = [0u8; 32];
279
280        let result_code = unsafe {
281            get_ledger_obj_field(
282                register_num,
283                sfield::Condition,
284                buffer.as_mut_ptr(),
285                buffer.len(),
286            )
287        };
288
289        match_result_code_with_expected_bytes_optional(result_code, 32, || Some(buffer.into()))
290    }
291
292    /// The destination address where the XRP is paid if the escrow is successful.
293    fn get_destination(&self, register_num: i32) -> Result<AccountID> {
294        ledger_object::get_account_id_field(register_num, sfield::Destination)
295    }
296
297    /// A hint indicating which page of the destination's owner directory links to this object, in
298    /// case the directory consists of multiple pages. Omitted on escrows created before enabling the fix1523 amendment.
299    fn get_destination_node(&self, register_num: i32) -> Result<Option<Hash256>> {
300        ledger_object::get_hash_256_field_optional(register_num, sfield::DestinationNode)
301    }
302
303    /// An arbitrary tag to further specify the destination for this escrow, such as a hosted
304    /// recipient at the destination address.
305    fn get_destination_tag(&self, register_num: i32) -> Result<Option<u32>> {
306        ledger_object::get_u32_field_optional(register_num, sfield::DestinationTag)
307    }
308
309    /// The time, in seconds since the Ripple Epoch, after which this escrow can be finished. Any
310    /// EscrowFinish transaction before this time fails. (Specifically, this is compared with the
311    /// close time of the previous validated ledger.)
312    fn get_finish_after(&self, register_num: i32) -> Result<Option<u32>> {
313        ledger_object::get_u32_field_optional(register_num, sfield::FinishAfter)
314    }
315
316    // TODO: Implement this function.
317    // /// The value 0x0075, mapped to the string Escrow, indicates that this is an Escrow entry.
318    // fn get_ledger_entry_type(&self, register_num: i32) -> Result<LedgerEntryType> {
319    //     return Ok(LedgerEntryType::Escrow);
320    // }
321
322    /// A hint indicating which page of the sender's owner directory links to this entry, in case
323    /// the directory consists of multiple pages.
324    fn get_owner_node(&self, register_num: i32) -> Result<Hash256> {
325        ledger_object::get_hash_256_field(register_num, sfield::OwnerNode)
326    }
327
328    /// The identifying hash of the transaction that most recently modified this entry.
329    fn get_previous_txn_id(&self, register_num: i32) -> Result<Hash256> {
330        ledger_object::get_hash_256_field(register_num, sfield::PreviousTxnID)
331    }
332
333    /// The index of the ledger that contains the transaction that most recently modified this
334    /// entry.
335    fn get_previous_txn_lgr_seq(&self, register_num: i32) -> Result<u32> {
336        ledger_object::get_u32_field(register_num, sfield::PreviousTxnLgrSeq)
337    }
338
339    /// An arbitrary tag to further specify the source for this escrow, such as a hosted recipient
340    /// at the owner's address.
341    fn get_source_tag(&self, register_num: i32) -> Result<Option<u32>> {
342        ledger_object::get_u32_field_optional(register_num, sfield::SourceTag)
343    }
344
345    /// The WASM code that is executing.
346    fn get_finish_function(&self, register_num: i32) -> Result<Option<Blob>> {
347        ledger_object::get_blob_field_optional(register_num, sfield::FinishFunction)
348    }
349
350    /// Retrieves the contract data from the specified ledger object.
351    ///
352    /// This function fetches the `data` field from the ledger object at the specified register
353    /// and returns it as a ContractData structure. The data is read into a fixed-size buffer
354    /// of XRPL_CONTRACT_DATA_SIZE.
355    ///
356    /// # Arguments
357    ///
358    /// * `register_num` - The register number where the ledger object is stored
359    ///
360    /// # Returns
361    ///
362    /// Returns a `Result<ContractData>` where:
363    /// * `Ok(ContractData)` - Contains the retrieved data and its actual length
364    /// * `Err(Error)` - If the retrieval operation failed
365    fn get_data(&self, register_num: i32) -> Result<ContractData> {
366        let mut data: [u8; XRPL_CONTRACT_DATA_SIZE] = [0; XRPL_CONTRACT_DATA_SIZE];
367
368        let result_code = unsafe {
369            get_ledger_obj_field(register_num, sfield::Data, data.as_mut_ptr(), data.len())
370        };
371
372        match result_code {
373            code if code >= 0 => Ok(ContractData {
374                data,
375                len: code as usize,
376            }),
377            code => Err(Error::from_code(code)),
378        }
379    }
380}