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}