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}