xrpl_wasm_stdlib/core/ledger_objects/
traits.rs

1use crate::core::ledger_objects::{current_ledger_object, ledger_object};
2use crate::core::types::account_id::AccountID;
3use crate::core::types::amount::Amount;
4use crate::core::types::blob::{
5    Blob, CONDITION_BLOB_SIZE, ConditionBlob, DEFAULT_BLOB_SIZE, UriBlob,
6};
7use crate::core::types::contract_data::{ContractData, XRPL_CONTRACT_DATA_SIZE};
8use crate::core::types::public_key::PUBLIC_KEY_BUFFER_SIZE;
9use crate::core::types::uint::{Hash128, Hash256};
10
11/// This module provides traits for interacting with XRP Ledger objects.
12///
13/// It defines common interfaces for accessing and manipulating different types of ledger objects,
14/// particularly focusing on Escrow objects. The traits provide methods to get and set various
15/// fields of ledger objects, with separate traits for current ledger objects and general ledger objects.
16use crate::host::error_codes::{
17    match_result_code, match_result_code_optional, match_result_code_with_expected_bytes,
18};
19use crate::host::{Error, get_current_ledger_obj_field, get_ledger_obj_field, update_data};
20use crate::host::{Result, Result::Err, Result::Ok};
21use crate::sfield;
22
23/// Trait providing access to common fields present in all ledger objects.
24///
25/// This trait defines methods to access standard fields that are common across
26/// different types of ledger objects in the XRP Ledger.
27pub trait LedgerObjectCommonFields {
28    // NOTE: `get_ledger_index()` is not in this trait because `sfLedgerIndex` is not actually a field on a ledger
29    // object (it's a synthetic field that maps to the `index` field, which is the unique ID of an object in the
30    // ledger's state tree). See https://github.com/XRPLF/rippled/issues/3649 for more context.
31
32    /// Returns the slot number (register number) where the ledger object is stored.
33    ///
34    /// This number is used to identify and access the specific ledger object
35    /// when retrieving or modifying its fields.
36    ///
37    /// # Returns
38    ///
39    /// The slot number as an i32 value
40    fn get_slot_num(&self) -> i32;
41
42    /// Retrieves the flags field of the ledger object.
43    ///
44    /// # Arguments
45    ///
46    /// * `register_num` - The register number where the ledger object is stored
47    ///
48    /// # Returns
49    ///
50    /// The flags as a u32 value
51    fn get_flags(&self) -> Result<u32> {
52        ledger_object::get_field(self.get_slot_num(), sfield::Flags)
53    }
54
55    /// Retrieves the ledger entry type of the object.
56    ///
57    /// The value 0x0075, mapped to the string Escrow, indicates that this is an Escrow entry.
58    ///
59    /// # Returns
60    ///
61    /// The ledger entry type as a u16 value
62    fn get_ledger_entry_type(&self) -> Result<u16> {
63        current_ledger_object::get_field(sfield::LedgerEntryType)
64    }
65}
66
67/// Trait providing access to common fields in the current ledger object.
68///
69/// This trait defines methods to access standard fields that are common across
70/// different types of ledger objects, specifically for the current ledger object
71/// being processed.
72pub trait CurrentLedgerObjectCommonFields {
73    // NOTE: `get_ledger_index()` is not in this trait because `sfLedgerIndex` is not actually a field on a ledger
74    // object (it's a synthetic field that maps to the `index` field, which is the unique ID of an object in the
75    // ledger's state tree). See https://github.com/XRPLF/rippled/issues/3649 for more context.
76
77    /// Retrieves the flags field of the current ledger object.
78    ///
79    /// # Returns
80    ///
81    /// The flags as a u32 value
82    fn get_flags(&self) -> Result<u32> {
83        current_ledger_object::get_field(sfield::Flags)
84    }
85
86    /// Retrieves the ledger entry type of the current ledger object.
87    ///
88    /// The value 0x0075, mapped to the string Escrow, indicates that this is an Escrow entry.
89    ///
90    /// # Returns
91    ///
92    /// The ledger entry type as a u16 value
93    fn get_ledger_entry_type(&self) -> Result<u16> {
94        current_ledger_object::get_field(sfield::LedgerEntryType)
95    }
96}
97
98/// Trait providing access to fields specific to Escrow objects in the current ledger.
99///
100/// This trait extends `CurrentLedgerObjectCommonFields` and provides methods to access
101/// fields that are specific to Escrow objects in the current ledger being processed.
102pub trait CurrentEscrowFields: CurrentLedgerObjectCommonFields {
103    /// The address of the owner (sender) of this escrow. This is the account that provided the XRP
104    /// and gets it back if the escrow is canceled.
105    fn get_account(&self) -> Result<AccountID> {
106        current_ledger_object::get_field(sfield::Account)
107    }
108
109    /// The amount currently held in the escrow (could be XRP, IOU, or MPT).
110    fn get_amount(&self) -> Result<Amount> {
111        current_ledger_object::get_field(sfield::Amount)
112    }
113
114    /// The escrow can be canceled if and only if this field is present and the time it specifies
115    /// has passed. Specifically, this is specified as seconds since the Ripple Epoch and it
116    /// "has passed" if it's earlier than the close time of the previous validated ledger.
117    fn get_cancel_after(&self) -> Result<Option<u32>> {
118        current_ledger_object::get_field_optional(sfield::CancelAfter)
119    }
120
121    /// A PREIMAGE-SHA-256 crypto-condition in full crypto-condition format. If present, the EscrowFinish
122    /// transaction must contain a fulfillment that satisfies this condition.
123    fn get_condition(&self) -> Result<Option<ConditionBlob>> {
124        let mut buffer = [0u8; CONDITION_BLOB_SIZE];
125
126        let result_code = unsafe {
127            get_current_ledger_obj_field(sfield::Condition, buffer.as_mut_ptr(), buffer.len())
128        };
129
130        match_result_code_optional(result_code, || {
131            if result_code > 0 {
132                let blob = ConditionBlob {
133                    data: buffer,
134                    len: result_code as usize,
135                };
136                Some(blob)
137            } else {
138                None
139            }
140        })
141    }
142
143    /// The destination address where the XRP is paid if the escrow is successful.
144    fn get_destination(&self) -> Result<AccountID> {
145        current_ledger_object::get_field(sfield::Destination)
146    }
147
148    /// A hint indicating which page of the destination's owner directory links to this object, in
149    /// case the directory consists of multiple pages. Omitted on escrows created before enabling the fix1523 amendment.
150    fn get_destination_node(&self) -> Result<Option<u64>> {
151        current_ledger_object::get_field_optional(sfield::DestinationNode)
152    }
153
154    /// An arbitrary tag to further specify the destination for this escrow, such as a hosted
155    /// recipient at the destination address.
156    fn get_destination_tag(&self) -> Result<Option<u32>> {
157        current_ledger_object::get_field_optional(sfield::DestinationTag)
158    }
159
160    /// The time, in seconds since the Ripple Epoch, after which this escrow can be finished. Any
161    /// EscrowFinish transaction before this time fails. (Specifically, this is compared with the
162    /// close time of the previous validated ledger.)
163    fn get_finish_after(&self) -> Result<Option<u32>> {
164        current_ledger_object::get_field_optional(sfield::FinishAfter)
165    }
166
167    // TODO: Implement this function.
168    // /// The value 0x0075, mapped to the string Escrow, indicates that this is an Escrow entry.
169    // fn get_ledger_entry_type(&self) -> Result<LedgerEntryType> {
170    //     return Ok(LedgerEntryType::Escrow);
171    // }
172
173    /// A hint indicating which page of the sender's owner directory links to this entry, in case
174    /// the directory consists of multiple pages.
175    fn get_owner_node(&self) -> Result<u64> {
176        current_ledger_object::get_field(sfield::OwnerNode)
177    }
178
179    /// The identifying hash of the transaction that most recently modified this entry.
180    fn get_previous_txn_id(&self) -> Result<Hash256> {
181        current_ledger_object::get_field(sfield::PreviousTxnID)
182    }
183
184    /// The index of the ledger that contains the transaction that most recently modified this
185    /// entry.
186    fn get_previous_txn_lgr_seq(&self) -> Result<u32> {
187        current_ledger_object::get_field(sfield::PreviousTxnLgrSeq)
188    }
189
190    /// An arbitrary tag to further specify the source for this escrow, such as a hosted recipient
191    /// at the owner's address.
192    fn get_source_tag(&self) -> Result<Option<u32>> {
193        current_ledger_object::get_field_optional(sfield::SourceTag)
194    }
195
196    /// The WASM code that is executing.
197    fn get_finish_function(&self) -> Result<Option<Blob<{ DEFAULT_BLOB_SIZE }>>> {
198        current_ledger_object::get_field_optional(sfield::FinishFunction)
199    }
200
201    /// Retrieves the contract `data` from the current escrow object.
202    ///
203    /// This function fetches the `data` field from the current ledger object and returns it as a
204    /// ContractData structure. The data is read into a fixed-size buffer of XRPL_CONTRACT_DATA_SIZE.
205    ///
206    /// # Returns
207    ///
208    /// Returns a `Result<ContractData>` where:
209    /// * `Ok(ContractData)` - Contains the retrieved data and its actual length
210    /// * `Err(Error)` - If the retrieval operation failed
211    fn get_data(&self) -> Result<ContractData> {
212        let mut data: [u8; XRPL_CONTRACT_DATA_SIZE] = [0; XRPL_CONTRACT_DATA_SIZE];
213
214        let result_code =
215            unsafe { get_current_ledger_obj_field(sfield::Data, data.as_mut_ptr(), data.len()) };
216
217        match result_code {
218            code if code >= 0 => Ok(ContractData {
219                data,
220                len: code as usize,
221            }),
222            code => Err(Error::from_code(code)),
223        }
224    }
225
226    /// Updates the contract data in the current escrow object.
227    ///
228    /// # Arguments
229    ///
230    /// * `data` - The contract data to update
231    ///
232    /// # Returns
233    ///
234    /// Returns a `Result<()>` where:
235    /// * `Ok(())` - The data was successfully updated
236    /// * `Err(Error)` - If the update operation failed
237    fn update_current_escrow_data(data: ContractData) -> Result<()> {
238        // TODO: Make sure rippled always deletes any existing data bytes in rippled, and sets the new
239        // length to be `data.len` (e.g., if the developer writes 2 bytes, then that's the new
240        // length and any old bytes are lost).
241        let result_code = unsafe { update_data(data.data.as_ptr(), data.len) };
242        match_result_code_with_expected_bytes(result_code, data.len, || ())
243    }
244}
245
246/// Trait providing access to fields specific to Escrow objects in any ledger.
247///
248/// This trait extends `LedgerObjectCommonFields` and provides methods to access
249/// fields that are specific to Escrow objects in any ledger, not just the current one.
250/// Each method requires a register number to identify which ledger object to access.
251pub trait EscrowFields: LedgerObjectCommonFields {
252    /// The address of the owner (sender) of this escrow. This is the account that provided the XRP
253    /// and gets it back if the escrow is canceled.
254    fn get_account(&self) -> Result<AccountID> {
255        ledger_object::get_field(self.get_slot_num(), sfield::Account)
256    }
257
258    /// The amount of XRP, in drops, currently held in the escrow.
259    fn get_amount(&self) -> Result<Amount> {
260        // Create a buffer large enough for any Amount type
261        const BUFFER_SIZE: usize = 48usize;
262        let mut buffer = [0u8; BUFFER_SIZE];
263
264        let result_code = unsafe {
265            get_ledger_obj_field(
266                self.get_slot_num(),
267                sfield::Amount,
268                buffer.as_mut_ptr(),
269                buffer.len(),
270            )
271        };
272
273        match_result_code(result_code, || Amount::from(buffer))
274    }
275
276    /// The escrow can be canceled if and only if this field is present and the time it specifies
277    /// has passed. Specifically, this is specified as seconds since the Ripple Epoch and it
278    /// "has passed" if it's earlier than the close time of the previous validated ledger.
279    fn get_cancel_after(&self) -> Result<Option<u32>> {
280        ledger_object::get_field_optional(self.get_slot_num(), sfield::CancelAfter)
281    }
282
283    /// A PREIMAGE-SHA-256 crypto-condition in full crypto-condition format. If present, the EscrowFinish
284    /// transaction must contain a fulfillment that satisfies this condition.
285    fn get_condition(&self) -> Result<Option<ConditionBlob>> {
286        let mut buffer = [0u8; CONDITION_BLOB_SIZE];
287
288        let result_code = unsafe {
289            get_ledger_obj_field(
290                self.get_slot_num(),
291                sfield::Condition,
292                buffer.as_mut_ptr(),
293                buffer.len(),
294            )
295        };
296
297        match_result_code_optional(result_code, || {
298            if result_code > 0 {
299                let blob = ConditionBlob {
300                    data: buffer,
301                    len: result_code as usize,
302                };
303                Some(blob)
304            } else {
305                None
306            }
307        })
308    }
309
310    /// The destination address where the XRP is paid if the escrow is successful.
311    fn get_destination(&self) -> Result<AccountID> {
312        ledger_object::get_field(self.get_slot_num(), sfield::Destination)
313    }
314
315    /// A hint indicating which page of the destination's owner directory links to this object, in
316    /// case the directory consists of multiple pages. Omitted on escrows created before enabling the fix1523 amendment.
317    fn get_destination_node(&self) -> Result<Option<u64>> {
318        ledger_object::get_field_optional(self.get_slot_num(), sfield::DestinationNode)
319    }
320
321    /// An arbitrary tag to further specify the destination for this escrow, such as a hosted
322    /// recipient at the destination address.
323    fn get_destination_tag(&self) -> Result<Option<u32>> {
324        ledger_object::get_field_optional(self.get_slot_num(), sfield::DestinationTag)
325    }
326
327    /// The time, in seconds since the Ripple Epoch, after which this escrow can be finished. Any
328    /// EscrowFinish transaction before this time fails. (Specifically, this is compared with the
329    /// close time of the previous validated ledger.)
330    fn get_finish_after(&self) -> Result<Option<u32>> {
331        ledger_object::get_field_optional(self.get_slot_num(), sfield::FinishAfter)
332    }
333
334    // TODO: Implement this function.
335    // /// The value 0x0075, mapped to the string Escrow, indicates that this is an Escrow entry.
336    // fn get_ledger_entry_type(&self) -> Result<LedgerEntryType> {
337    //     return Ok(LedgerEntryType::Escrow);
338    // }
339
340    /// A hint indicating which page of the sender's owner directory links to this entry, in case
341    /// the directory consists of multiple pages.
342    fn get_owner_node(&self) -> Result<u64> {
343        ledger_object::get_field(self.get_slot_num(), sfield::OwnerNode)
344    }
345
346    /// The identifying hash of the transaction that most recently modified this entry.
347    fn get_previous_txn_id(&self) -> Result<Hash256> {
348        ledger_object::get_field(self.get_slot_num(), sfield::PreviousTxnID)
349    }
350
351    /// The index of the ledger that contains the transaction that most recently modified this
352    /// entry.
353    fn get_previous_txn_lgr_seq(&self) -> Result<u32> {
354        ledger_object::get_field(self.get_slot_num(), sfield::PreviousTxnLgrSeq)
355    }
356
357    /// An arbitrary tag to further specify the source for this escrow, such as a hosted recipient
358    /// at the owner's address.
359    fn get_source_tag(&self) -> Result<Option<u32>> {
360        ledger_object::get_field_optional(self.get_slot_num(), sfield::SourceTag)
361    }
362
363    /// The WASM code that is executing.
364    fn get_finish_function(&self) -> Result<Option<Blob<{ DEFAULT_BLOB_SIZE }>>> {
365        ledger_object::get_field_optional(self.get_slot_num(), sfield::FinishFunction)
366    }
367
368    /// Retrieves the contract data from the specified ledger object.
369    ///
370    /// This function fetches the `data` field from the ledger object at the specified register
371    /// and returns it as a ContractData structure. The data is read into a fixed-size buffer
372    /// of XRPL_CONTRACT_DATA_SIZE.
373    ///
374    /// # Arguments
375    ///
376    /// * `register_num` - The register number where the ledger object is stored
377    ///
378    /// # Returns
379    ///
380    /// Returns a `Result<ContractData>` where:
381    /// * `Ok(ContractData)` - Contains the retrieved data and its actual length
382    /// * `Err(Error)` - If the retrieval operation failed
383    fn get_data(&self) -> Result<ContractData> {
384        let mut data: [u8; XRPL_CONTRACT_DATA_SIZE] = [0; XRPL_CONTRACT_DATA_SIZE];
385
386        let result_code = unsafe {
387            get_ledger_obj_field(
388                self.get_slot_num(),
389                sfield::Data,
390                data.as_mut_ptr(),
391                data.len(),
392            )
393        };
394
395        match result_code {
396            code if code >= 0 => Ok(ContractData {
397                data,
398                len: code as usize,
399            }),
400            code => Err(Error::from_code(code)),
401        }
402    }
403}
404
405/// Trait providing access to fields specific to AccountRoot objects in any ledger.
406///
407/// This trait extends `LedgerObjectCommonFields` and provides methods to access
408/// fields that are specific to Escrow objects in any ledger, not just the current one.
409/// Each method requires a register number to identify which ledger object to access.
410pub trait AccountFields: LedgerObjectCommonFields {
411    /// The identifying address of the account.
412    fn get_account(&self) -> Result<AccountID> {
413        ledger_object::get_field(self.get_slot_num(), sfield::Account)
414    }
415
416    /// AccountTxnID field for the account.
417    fn account_txn_id(&self) -> Result<Option<Hash256>> {
418        ledger_object::get_field_optional(self.get_slot_num(), sfield::AccountTxnID)
419    }
420
421    /// The ledger entry ID of the corresponding AMM ledger entry. Set during account creation; cannot be modified.
422    /// If present, indicates that this is a special AMM AccountRoot; always omitted on non-AMM accounts.
423    /// (Added by the AMM amendment)
424    fn amm_id(&self) -> Result<Option<Hash256>> {
425        ledger_object::get_field_optional(self.get_slot_num(), sfield::AMMID)
426    }
427
428    /// The account's current XRP balance in drops.
429    fn balance(&self) -> Result<Option<Amount>> {
430        ledger_object::get_field_optional(self.get_slot_num(), sfield::Balance)
431    }
432
433    /// How many total of this account's issued non-fungible tokens have been burned.
434    /// This number is always equal or less than MintedNFTokens.
435    fn burned_nf_tokens(&self) -> Result<Option<u32>> {
436        ledger_object::get_field_optional(self.get_slot_num(), sfield::BurnedNFTokens)
437    }
438
439    /// A domain associated with this account. In JSON, this is the hexadecimal for the ASCII representation of the
440    /// domain. Cannot be more than 256 bytes in length.
441    fn domain(&self) -> Result<Option<UriBlob>> {
442        ledger_object::get_field_optional(self.get_slot_num(), sfield::Domain)
443    }
444
445    /// The MD5 hash of an email address. Clients can use this to look up an avatar through services such as Gravatar.
446    fn email_hash(&self) -> Result<Option<Hash128>> {
447        ledger_object::get_field_optional(self.get_slot_num(), sfield::EmailHash)
448    }
449
450    /// The account's Sequence Number at the time it minted its first non-fungible-token.
451    /// (Added by the fixNFTokenRemint amendment)
452    fn first_nf_token_sequence(&self) -> Result<Option<u32>> {
453        ledger_object::get_field_optional(self.get_slot_num(), sfield::FirstNFTokenSequence)
454    }
455
456    /// The value 0x0061, mapped to the string AccountRoot, indicates that this is an AccountRoot object.
457    fn ledger_entry_type(&self) -> Result<u16> {
458        ledger_object::get_field(self.get_slot_num(), sfield::LedgerEntryType)
459    }
460
461    /// A public key that may be used to send encrypted messages to this account. In JSON, uses hexadecimal.
462    /// Must be exactly 33 bytes, with the first byte indicating the key type: 0x02 or 0x03 for secp256k1 keys,
463    /// 0xED for Ed25519 keys.
464    fn message_key(&self) -> Result<Option<Blob<{ PUBLIC_KEY_BUFFER_SIZE }>>> {
465        ledger_object::get_field_optional(self.get_slot_num(), sfield::MessageKey)
466    }
467
468    /// How many total non-fungible tokens have been minted by and on behalf of this account.
469    /// (Added by the NonFungibleTokensV1_1 amendment)
470    fn minted_nf_tokens(&self) -> Result<Option<u32>> {
471        ledger_object::get_field_optional(self.get_slot_num(), sfield::MintedNFTokens)
472    }
473
474    /// Another account that can mint non-fungible tokens on behalf of this account.
475    /// (Added by the NonFungibleTokensV1_1 amendment)
476    fn nf_token_minter(&self) -> Result<Option<AccountID>> {
477        ledger_object::get_field_optional(self.get_slot_num(), sfield::NFTokenMinter)
478    }
479
480    /// The number of objects this account owns in the ledger, which contributes to its owner reserve.
481    fn owner_count(&self) -> Result<u32> {
482        ledger_object::get_field(self.get_slot_num(), sfield::OwnerCount)
483    }
484
485    /// The identifying hash of the transaction that most recently modified this object.
486    fn previous_txn_id(&self) -> Result<Hash256> {
487        ledger_object::get_field(self.get_slot_num(), sfield::PreviousTxnID)
488    }
489
490    /// The index of the ledger that contains the transaction that most recently modified this object.
491    fn previous_txn_lgr_seq(&self) -> Result<u32> {
492        ledger_object::get_field(self.get_slot_num(), sfield::PreviousTxnLgrSeq)
493    }
494
495    /// The address of a key pair that can be used to sign transactions for this account instead of the master key.
496    /// Use a SetRegularKey transaction to change this value.
497    fn regular_key(&self) -> Result<Option<AccountID>> {
498        ledger_object::get_field_optional(self.get_slot_num(), sfield::RegularKey)
499    }
500
501    /// The sequence number of the next valid transaction for this account.
502    fn sequence(&self) -> Result<u32> {
503        ledger_object::get_field(self.get_slot_num(), sfield::Sequence)
504    }
505
506    /// How many Tickets this account owns in the ledger. This is updated automatically to ensure that
507    /// the account stays within the hard limit of 250 Tickets at a time. This field is omitted if the account has zero
508    /// Tickets. (Added by the TicketBatch amendment.)
509    fn ticket_count(&self) -> Result<Option<u32>> {
510        ledger_object::get_field_optional(self.get_slot_num(), sfield::TicketCount)
511    }
512
513    /// How many significant digits to use for exchange rates of Offers involving currencies issued by this address.
514    /// Valid values are 3 to 15, inclusive. (Added by the TickSize amendment.)
515    fn tick_size(&self) -> Result<Option<u8>> {
516        ledger_object::get_field_optional(self.get_slot_num(), sfield::TickSize)
517    }
518
519    /// A transfer fee to charge other users for sending currency issued by this account to each other.
520    fn transfer_rate(&self) -> Result<Option<u32>> {
521        ledger_object::get_field_optional(self.get_slot_num(), sfield::TransferRate)
522    }
523
524    /// An arbitrary 256-bit value that users can set.
525    fn wallet_locator(&self) -> Result<Option<Hash256>> {
526        ledger_object::get_field_optional(self.get_slot_num(), sfield::WalletLocator)
527    }
528}
529
530#[cfg(test)]
531mod tests {
532    use super::*;
533    use crate::core::ledger_objects::current_escrow::CurrentEscrow;
534    use crate::core::ledger_objects::escrow::Escrow;
535
536    #[test]
537    fn test_current_escrow_get_condition_returns_some_with_data() {
538        // When the mock host function returns a positive value (buffer length),
539        // get_condition should return Ok(Some(ConditionBlob))
540        let escrow = CurrentEscrow;
541        let result = escrow.get_condition();
542
543        // The mock returns buffer.len() which is CONDITION_BLOB_SIZE (128)
544        assert!(result.is_ok());
545        let condition_opt = result.unwrap();
546        assert!(condition_opt.is_some());
547
548        let condition = condition_opt.unwrap();
549        assert_eq!(condition.len, CONDITION_BLOB_SIZE);
550        assert_eq!(condition.capacity(), CONDITION_BLOB_SIZE);
551    }
552
553    #[test]
554    fn test_escrow_get_condition_returns_some_with_data() {
555        // When the mock host function returns a positive value (buffer length),
556        // get_condition should return Ok(Some(ConditionBlob))
557        let escrow = Escrow { slot_num: 0 };
558        let result = escrow.get_condition();
559
560        // The mock returns buffer.len() which is CONDITION_BLOB_SIZE (128)
561        assert!(result.is_ok());
562        let condition_opt = result.unwrap();
563        assert!(condition_opt.is_some());
564
565        let condition = condition_opt.unwrap();
566        assert_eq!(condition.len, CONDITION_BLOB_SIZE);
567        assert_eq!(condition.capacity(), CONDITION_BLOB_SIZE);
568    }
569
570    #[test]
571    fn test_escrow_get_condition_with_different_slots() {
572        // Verify that get_condition works with different slot numbers
573        let escrow1 = Escrow { slot_num: 0 };
574        let escrow2 = Escrow { slot_num: 1 };
575        let escrow3 = Escrow { slot_num: 5 };
576
577        let result1 = escrow1.get_condition();
578        let result2 = escrow2.get_condition();
579        let result3 = escrow3.get_condition();
580
581        assert!(result1.is_ok());
582        assert!(result2.is_ok());
583        assert!(result3.is_ok());
584
585        // All should return Some with the same buffer size
586        assert!(result1.unwrap().is_some());
587        assert!(result2.unwrap().is_some());
588        assert!(result3.unwrap().is_some());
589    }
590
591    #[test]
592    fn test_current_escrow_struct_get_condition() {
593        // Test the actual CurrentEscrow struct implementation
594        let escrow = CurrentEscrow;
595        let result = escrow.get_condition();
596
597        assert!(result.is_ok());
598        if let Some(condition) = result.unwrap() {
599            assert_eq!(condition.capacity(), CONDITION_BLOB_SIZE);
600        }
601    }
602}