1pub mod account_root;
2pub mod array_object;
3pub mod current_escrow;
4pub mod escrow;
5pub mod traits;
6
7use crate::core::types::uint::{HASH160_SIZE, HASH192_SIZE, Hash160, Hash192};
8use crate::host::error_codes::{
9 match_result_code_with_expected_bytes, match_result_code_with_expected_bytes_optional,
10};
11use crate::host::{Result, get_current_ledger_obj_field, get_ledger_obj_field};
12use crate::sfield::SField;
13
14pub trait LedgerObjectFieldGetter: Sized {
63 fn get_from_current_ledger_obj<const CODE: i32>(field: SField<Self, CODE>) -> Result<Self>;
75
76 fn get_from_current_ledger_obj_optional<const CODE: i32>(
89 field: SField<Self, CODE>,
90 ) -> Result<Option<Self>>;
91
92 fn get_from_ledger_obj<const CODE: i32>(
105 register_num: i32,
106 field: SField<Self, CODE>,
107 ) -> Result<Self>;
108
109 fn get_from_ledger_obj_optional<const CODE: i32>(
123 register_num: i32,
124 field: SField<Self, CODE>,
125 ) -> Result<Option<Self>>;
126}
127
128trait FixedSizeFieldType: Sized {
141 const SIZE: usize;
143}
144
145impl FixedSizeFieldType for u8 {
146 const SIZE: usize = 1;
147}
148
149impl FixedSizeFieldType for u16 {
150 const SIZE: usize = 2;
151}
152
153impl FixedSizeFieldType for u32 {
154 const SIZE: usize = 4;
155}
156
157impl FixedSizeFieldType for u64 {
158 const SIZE: usize = 8;
159}
160
161impl<T: FixedSizeFieldType> LedgerObjectFieldGetter for T {
175 #[inline]
176 fn get_from_current_ledger_obj<const CODE: i32>(field: SField<Self, CODE>) -> Result<Self> {
177 let mut value = core::mem::MaybeUninit::<T>::uninit();
178 let result_code = unsafe {
179 get_current_ledger_obj_field(i32::from(field), value.as_mut_ptr().cast(), T::SIZE)
180 };
181 match_result_code_with_expected_bytes(result_code, T::SIZE, || unsafe {
182 value.assume_init()
183 })
184 }
185
186 #[inline]
187 fn get_from_current_ledger_obj_optional<const CODE: i32>(
188 field: SField<Self, CODE>,
189 ) -> Result<Option<Self>> {
190 let mut value = core::mem::MaybeUninit::<T>::uninit();
191 let result_code = unsafe {
192 get_current_ledger_obj_field(i32::from(field), value.as_mut_ptr().cast(), T::SIZE)
193 };
194 match_result_code_with_expected_bytes_optional(result_code, T::SIZE, || {
195 Some(unsafe { value.assume_init() })
196 })
197 }
198
199 #[inline]
200 fn get_from_ledger_obj<const CODE: i32>(
201 register_num: i32,
202 field: SField<Self, CODE>,
203 ) -> Result<Self> {
204 let mut value = core::mem::MaybeUninit::<T>::uninit();
205 let result_code = unsafe {
206 get_ledger_obj_field(
207 register_num,
208 i32::from(field),
209 value.as_mut_ptr().cast(),
210 T::SIZE,
211 )
212 };
213 match_result_code_with_expected_bytes(result_code, T::SIZE, || unsafe {
214 value.assume_init()
215 })
216 }
217
218 #[inline]
219 fn get_from_ledger_obj_optional<const CODE: i32>(
220 register_num: i32,
221 field: SField<Self, CODE>,
222 ) -> Result<Option<Self>> {
223 let mut value = core::mem::MaybeUninit::<T>::uninit();
224 let result_code = unsafe {
225 get_ledger_obj_field(
226 register_num,
227 i32::from(field),
228 value.as_mut_ptr().cast(),
229 T::SIZE,
230 )
231 };
232 match_result_code_with_expected_bytes_optional(result_code, T::SIZE, || {
233 Some(unsafe { value.assume_init() })
234 })
235 }
236}
237
238impl LedgerObjectFieldGetter for Hash160 {
248 #[inline]
249 fn get_from_current_ledger_obj<const CODE: i32>(field: SField<Self, CODE>) -> Result<Self> {
250 let mut buffer = core::mem::MaybeUninit::<[u8; HASH160_SIZE]>::uninit();
251 let result_code = unsafe {
252 get_current_ledger_obj_field(i32::from(field), buffer.as_mut_ptr().cast(), HASH160_SIZE)
253 };
254 match_result_code_with_expected_bytes(result_code, HASH160_SIZE, || {
255 Hash160::from(unsafe { buffer.assume_init() })
256 })
257 }
258
259 #[inline]
260 fn get_from_current_ledger_obj_optional<const CODE: i32>(
261 field: SField<Self, CODE>,
262 ) -> Result<Option<Self>> {
263 let mut buffer = core::mem::MaybeUninit::<[u8; HASH160_SIZE]>::uninit();
264 let result_code = unsafe {
265 get_current_ledger_obj_field(i32::from(field), buffer.as_mut_ptr().cast(), HASH160_SIZE)
266 };
267 match_result_code_with_expected_bytes_optional(result_code, HASH160_SIZE, || {
268 Some(Hash160::from(unsafe { buffer.assume_init() }))
269 })
270 }
271
272 #[inline]
273 fn get_from_ledger_obj<const CODE: i32>(
274 register_num: i32,
275 field: SField<Self, CODE>,
276 ) -> Result<Self> {
277 let mut buffer = core::mem::MaybeUninit::<[u8; HASH160_SIZE]>::uninit();
278 let result_code = unsafe {
279 get_ledger_obj_field(
280 register_num,
281 i32::from(field),
282 buffer.as_mut_ptr().cast(),
283 HASH160_SIZE,
284 )
285 };
286 match_result_code_with_expected_bytes(result_code, HASH160_SIZE, || {
287 Hash160::from(unsafe { buffer.assume_init() })
288 })
289 }
290
291 #[inline]
292 fn get_from_ledger_obj_optional<const CODE: i32>(
293 register_num: i32,
294 field: SField<Self, CODE>,
295 ) -> Result<Option<Self>> {
296 let mut buffer = core::mem::MaybeUninit::<[u8; HASH160_SIZE]>::uninit();
297 let result_code = unsafe {
298 get_ledger_obj_field(
299 register_num,
300 i32::from(field),
301 buffer.as_mut_ptr().cast(),
302 HASH160_SIZE,
303 )
304 };
305 match_result_code_with_expected_bytes_optional(result_code, HASH160_SIZE, || {
306 Some(Hash160::from(unsafe { buffer.assume_init() }))
307 })
308 }
309}
310
311impl LedgerObjectFieldGetter for Hash192 {
321 #[inline]
322 fn get_from_current_ledger_obj<const CODE: i32>(field: SField<Self, CODE>) -> Result<Self> {
323 let mut buffer = core::mem::MaybeUninit::<[u8; HASH192_SIZE]>::uninit();
324 let result_code = unsafe {
325 get_current_ledger_obj_field(i32::from(field), buffer.as_mut_ptr().cast(), HASH192_SIZE)
326 };
327 match_result_code_with_expected_bytes(result_code, HASH192_SIZE, || {
328 Hash192::from(unsafe { buffer.assume_init() })
329 })
330 }
331
332 #[inline]
333 fn get_from_current_ledger_obj_optional<const CODE: i32>(
334 field: SField<Self, CODE>,
335 ) -> Result<Option<Self>> {
336 let mut buffer = core::mem::MaybeUninit::<[u8; HASH192_SIZE]>::uninit();
337 let result_code = unsafe {
338 get_current_ledger_obj_field(i32::from(field), buffer.as_mut_ptr().cast(), HASH192_SIZE)
339 };
340 match_result_code_with_expected_bytes_optional(result_code, HASH192_SIZE, || {
341 Some(Hash192::from(unsafe { buffer.assume_init() }))
342 })
343 }
344
345 #[inline]
346 fn get_from_ledger_obj<const CODE: i32>(
347 register_num: i32,
348 field: SField<Self, CODE>,
349 ) -> Result<Self> {
350 let mut buffer = core::mem::MaybeUninit::<[u8; HASH192_SIZE]>::uninit();
351 let result_code = unsafe {
352 get_ledger_obj_field(
353 register_num,
354 i32::from(field),
355 buffer.as_mut_ptr().cast(),
356 HASH192_SIZE,
357 )
358 };
359 match_result_code_with_expected_bytes(result_code, HASH192_SIZE, || {
360 Hash192::from(unsafe { buffer.assume_init() })
361 })
362 }
363
364 #[inline]
365 fn get_from_ledger_obj_optional<const CODE: i32>(
366 register_num: i32,
367 field: SField<Self, CODE>,
368 ) -> Result<Option<Self>> {
369 let mut buffer = core::mem::MaybeUninit::<[u8; HASH192_SIZE]>::uninit();
370 let result_code = unsafe {
371 get_ledger_obj_field(
372 register_num,
373 i32::from(field),
374 buffer.as_mut_ptr().cast(),
375 HASH192_SIZE,
376 )
377 };
378 match_result_code_with_expected_bytes_optional(result_code, HASH192_SIZE, || {
379 Some(Hash192::from(unsafe { buffer.assume_init() }))
380 })
381 }
382}
383
384pub mod current_ledger_object {
385 use super::LedgerObjectFieldGetter;
386 use crate::host::Result;
387 use crate::sfield::SField;
388
389 #[inline]
412 pub fn get_field<T: LedgerObjectFieldGetter, const CODE: i32>(
413 field: SField<T, CODE>,
414 ) -> Result<T> {
415 T::get_from_current_ledger_obj(field)
416 }
417
418 #[inline]
431 pub fn get_field_optional<T: LedgerObjectFieldGetter, const CODE: i32>(
432 field: SField<T, CODE>,
433 ) -> Result<Option<T>> {
434 T::get_from_current_ledger_obj_optional(field)
435 }
436
437 #[cfg(test)]
438 mod tests {
439 use super::*;
440 use crate::core::types::account_id::{ACCOUNT_ID_SIZE, AccountID};
441 use crate::core::types::amount::{AMOUNT_SIZE, Amount};
442 use crate::core::types::blob::{Blob, PUBLIC_KEY_BLOB_SIZE, PublicKeyBlob};
443 use crate::core::types::currency::{CURRENCY_SIZE, Currency};
444 use crate::core::types::issue::Issue;
445 use crate::core::types::public_key::PUBLIC_KEY_BUFFER_SIZE;
446 use crate::core::types::uint::{
447 HASH128_SIZE, HASH160_SIZE, HASH192_SIZE, HASH256_SIZE, Hash128, Hash160, Hash192,
448 Hash256,
449 };
450 use crate::host::host_bindings_trait::MockHostBindings;
451 use crate::host::setup_mock;
452 use crate::sfield::{self, SField};
453 use mockall::predicate::{always, eq};
454
455 fn expect_current_field<
466 T: LedgerObjectFieldGetter + Send + std::fmt::Debug + PartialEq + 'static,
467 const CODE: i32,
468 >(
469 mock: &mut MockHostBindings,
470 field: SField<T, CODE>,
471 size: usize,
472 times: usize,
473 ) {
474 mock.expect_get_current_ledger_obj_field()
475 .with(eq(field), always(), eq(size))
476 .times(times)
477 .returning(move |_, buf, buf_size| {
478 unsafe { core::ptr::write_bytes(buf, 0, buf_size) };
479 size as i32
480 });
481 }
482
483 fn expect_current_field_short<
487 T: LedgerObjectFieldGetter + Send + std::fmt::Debug + PartialEq + 'static,
488 const CODE: i32,
489 >(
490 mock: &mut MockHostBindings,
491 field: SField<T, CODE>,
492 buf_size: usize,
493 returned: i32,
494 ) {
495 mock.expect_get_current_ledger_obj_field()
496 .with(eq(field), always(), eq(buf_size))
497 .times(1)
498 .returning(move |_, buf, buf_size| {
499 unsafe { core::ptr::write_bytes(buf, 0, buf_size) };
500 returned
501 });
502 }
503
504 #[test]
505 fn test_current_basic_types() {
506 let mut mock = MockHostBindings::new();
507
508 expect_current_field(&mut mock, sfield::LedgerEntryType, 2, 1);
509 expect_current_field(&mut mock, sfield::Flags, 4, 1);
510 expect_current_field(&mut mock, sfield::OwnerNode, 8, 1);
511
512 let _guard = setup_mock(mock);
513
514 assert!(u16::get_from_current_ledger_obj(sfield::LedgerEntryType).is_ok());
515 assert!(u32::get_from_current_ledger_obj(sfield::Flags).is_ok());
516 assert!(u64::get_from_current_ledger_obj(sfield::OwnerNode).is_ok());
517 }
518
519 #[test]
520 fn test_current_xrpl_types() {
521 let mut mock = MockHostBindings::new();
522
523 expect_current_field(&mut mock, sfield::Account, ACCOUNT_ID_SIZE, 1);
524 expect_current_field(&mut mock, sfield::Amount, AMOUNT_SIZE, 1);
525 expect_current_field(&mut mock, sfield::EmailHash, HASH128_SIZE, 1);
526 expect_current_field(&mut mock, sfield::PreviousTxnID, HASH256_SIZE, 1);
527 expect_current_field(&mut mock, sfield::PublicKey, PUBLIC_KEY_BLOB_SIZE, 1);
528 expect_current_field(&mut mock, sfield::TakerPaysCurrency, HASH160_SIZE, 1);
529 expect_current_field(&mut mock, sfield::MPTokenIssuanceID, HASH192_SIZE, 1);
530 expect_current_field(&mut mock, sfield::BaseAsset, CURRENCY_SIZE, 1);
531 expect_current_field_short(&mut mock, sfield::Asset, 40, 20);
532
533 let _guard = setup_mock(mock);
534
535 assert!(AccountID::get_from_current_ledger_obj(sfield::Account).is_ok());
536 assert!(Amount::get_from_current_ledger_obj(sfield::Amount).is_ok());
537 assert!(Hash128::get_from_current_ledger_obj(sfield::EmailHash).is_ok());
538 assert!(Hash256::get_from_current_ledger_obj(sfield::PreviousTxnID).is_ok());
539
540 let blob: PublicKeyBlob = Blob::get_from_current_ledger_obj(sfield::PublicKey).unwrap();
541 assert_eq!(blob.len, 33);
542
543 assert!(Hash160::get_from_current_ledger_obj(sfield::TakerPaysCurrency).is_ok());
544 assert!(Hash192::get_from_current_ledger_obj(sfield::MPTokenIssuanceID).is_ok());
545 assert!(Currency::get_from_current_ledger_obj(sfield::BaseAsset).is_ok());
546 assert!(Issue::get_from_current_ledger_obj(sfield::Asset).is_ok());
547 }
548
549 #[test]
550 fn test_current_optional_fields() {
551 let mut mock = MockHostBindings::new();
552
553 expect_current_field(&mut mock, sfield::Flags, 4, 1);
554 expect_current_field(&mut mock, sfield::Account, ACCOUNT_ID_SIZE, 1);
555 expect_current_field(&mut mock, sfield::Amount, AMOUNT_SIZE, 1);
556 expect_current_field(&mut mock, sfield::EmailHash, HASH128_SIZE, 1);
557 expect_current_field(&mut mock, sfield::PreviousTxnID, HASH256_SIZE, 1);
558 expect_current_field(&mut mock, sfield::TakerPaysCurrency, HASH160_SIZE, 1);
559 expect_current_field(&mut mock, sfield::MPTokenIssuanceID, HASH192_SIZE, 1);
560 expect_current_field(&mut mock, sfield::BaseAsset, CURRENCY_SIZE, 1);
561 expect_current_field(&mut mock, sfield::PublicKey, PUBLIC_KEY_BLOB_SIZE, 1);
562 expect_current_field_short(&mut mock, sfield::Asset, 40, 20);
563
564 let _guard = setup_mock(mock);
565
566 let result = u32::get_from_current_ledger_obj_optional(sfield::Flags);
567 assert!(result.is_ok());
568 assert!(result.unwrap().is_some());
569
570 let result = AccountID::get_from_current_ledger_obj_optional(sfield::Account);
571 assert!(result.is_ok());
572 assert!(result.unwrap().is_some());
573
574 let result = Amount::get_from_current_ledger_obj_optional(sfield::Amount);
575 assert!(result.is_ok());
576 assert!(result.unwrap().is_some());
577
578 let result = Hash128::get_from_current_ledger_obj_optional(sfield::EmailHash);
579 assert!(result.is_ok());
580 assert!(result.unwrap().is_some());
581
582 let result = Hash256::get_from_current_ledger_obj_optional(sfield::PreviousTxnID);
583 assert!(result.is_ok());
584 assert!(result.unwrap().is_some());
585
586 let result = Hash160::get_from_current_ledger_obj_optional(sfield::TakerPaysCurrency);
587 assert!(result.is_ok());
588 assert!(result.unwrap().is_some());
589
590 let result = Hash192::get_from_current_ledger_obj_optional(sfield::MPTokenIssuanceID);
591 assert!(result.is_ok());
592 assert!(result.unwrap().is_some());
593
594 let result = Currency::get_from_current_ledger_obj_optional(sfield::BaseAsset);
595 assert!(result.is_ok());
596 assert!(result.unwrap().is_some());
597
598 let result = PublicKeyBlob::get_from_current_ledger_obj_optional(sfield::PublicKey);
599 assert!(result.is_ok());
600 assert!(result.unwrap().is_some());
601
602 let result = Issue::get_from_current_ledger_obj_optional(sfield::Asset);
603 assert!(result.is_ok());
604 assert!(result.unwrap().is_some());
605 }
606
607 #[test]
610 fn test_current_module_convenience_functions() {
611 let mut mock = MockHostBindings::new();
612
613 expect_current_field(&mut mock, sfield::Flags, 4, 2);
614 expect_current_field(&mut mock, sfield::Account, ACCOUNT_ID_SIZE, 1);
615
616 let _guard = setup_mock(mock);
617
618 assert!(get_field(sfield::Flags).is_ok());
619 assert!(get_field(sfield::Account).is_ok());
620
621 let result = get_field_optional(sfield::Flags);
622 assert!(result.is_ok());
623 assert!(result.unwrap().is_some());
624 }
625
626 #[test]
627 fn test_type_sizes() {
628 let mut mock = MockHostBindings::new();
629
630 expect_current_field(&mut mock, sfield::EmailHash, HASH128_SIZE, 1);
631 expect_current_field(&mut mock, sfield::PreviousTxnID, HASH256_SIZE, 1);
632 expect_current_field(&mut mock, sfield::Account, ACCOUNT_ID_SIZE, 1);
633 expect_current_field(&mut mock, sfield::PublicKey, PUBLIC_KEY_BUFFER_SIZE, 1);
634
635 let _guard = setup_mock(mock);
636
637 let hash128 = Hash128::get_from_current_ledger_obj(sfield::EmailHash).unwrap();
638 assert_eq!(hash128.as_bytes().len(), HASH128_SIZE);
639
640 let hash256 = Hash256::get_from_current_ledger_obj(sfield::PreviousTxnID).unwrap();
641 assert_eq!(hash256.as_bytes().len(), HASH256_SIZE);
642
643 let account = AccountID::get_from_current_ledger_obj(sfield::Account).unwrap();
644 assert_eq!(account.0.len(), ACCOUNT_ID_SIZE);
645
646 let blob: Blob<PUBLIC_KEY_BUFFER_SIZE> =
647 Blob::get_from_current_ledger_obj(sfield::PublicKey).unwrap();
648 assert_eq!(blob.len, PUBLIC_KEY_BUFFER_SIZE);
649 assert_eq!(blob.data.len(), PUBLIC_KEY_BUFFER_SIZE);
650 }
651
652 #[test]
656 fn test_issue_decodes_xrp_variant() {
657 let mut mock = MockHostBindings::new();
658 expect_current_field_short(&mut mock, sfield::Asset, 40, 20);
659
660 let _guard = setup_mock(mock);
661
662 let issue = Issue::get_from_current_ledger_obj(sfield::Asset).unwrap();
663 assert!(matches!(issue, Issue::XRP(_)));
664 }
665
666 #[test]
667 fn test_issue_decodes_mpt_variant() {
668 let mut mock = MockHostBindings::new();
669 mock.expect_get_current_ledger_obj_field()
670 .with(eq(sfield::Asset), always(), eq(40))
671 .times(1)
672 .returning(|_, buf, _| {
673 let slice = unsafe { core::slice::from_raw_parts_mut(buf, 24) };
675 slice[0..4].copy_from_slice(&42u32.to_be_bytes());
676 slice[4..24].fill(0xAB);
677 24
678 });
679
680 let _guard = setup_mock(mock);
681
682 let issue = Issue::get_from_current_ledger_obj(sfield::Asset).unwrap();
683 match issue {
684 Issue::MPT(mpt) => {
685 assert_eq!(mpt.mpt_id().get_sequence_num(), 42);
686 assert_eq!(mpt.mpt_id().get_issuer(), AccountID::from([0xAB; 20]));
687 }
688 _ => panic!("expected MPT variant"),
689 }
690 }
691
692 #[test]
693 fn test_issue_decodes_iou_variant() {
694 let mut mock = MockHostBindings::new();
695 mock.expect_get_current_ledger_obj_field()
696 .with(eq(sfield::Asset), always(), eq(40))
697 .times(1)
698 .returning(|_, buf, _| {
699 let slice = unsafe { core::slice::from_raw_parts_mut(buf, 40) };
701 slice[0..20].fill(0xCC);
702 slice[20..40].fill(0xDD);
703 40
704 });
705
706 let _guard = setup_mock(mock);
707
708 let issue = Issue::get_from_current_ledger_obj(sfield::Asset).unwrap();
709 match issue {
710 Issue::IOU(iou) => {
711 let bytes = iou.as_bytes();
712 assert_eq!(&bytes[..20], &[0xCC; 20]);
713 assert_eq!(&bytes[20..], &[0xDD; 20]);
714 }
715 _ => panic!("expected IOU variant"),
716 }
717 }
718
719 #[test]
723 fn test_amount_decodes_xrp_variant() {
724 let mut mock = MockHostBindings::new();
725 mock.expect_get_current_ledger_obj_field()
726 .with(eq(sfield::Amount), always(), eq(48))
727 .times(1)
728 .returning(|_, buf, size| {
729 let slice = unsafe { core::slice::from_raw_parts_mut(buf, size) };
732 slice.fill(0);
733 let mut be = 1000u64.to_be_bytes();
734 be[0] |= 0x40; slice[0..8].copy_from_slice(&be);
736 8
737 });
738
739 let _guard = setup_mock(mock);
740
741 let amount = Amount::get_from_current_ledger_obj(sfield::Amount).unwrap();
742 assert!(matches!(amount, Amount::XRP { num_drops: 1000 }));
743 }
744
745 #[test]
746 fn test_amount_decodes_mpt_variant() {
747 let mut mock = MockHostBindings::new();
748 mock.expect_get_current_ledger_obj_field()
749 .with(eq(sfield::Amount), always(), eq(48))
750 .times(1)
751 .returning(|_, buf, size| {
752 let slice = unsafe { core::slice::from_raw_parts_mut(buf, size) };
756 slice.fill(0);
757 slice[0] = 0x60;
758 slice[1..9].copy_from_slice(&100u64.to_be_bytes());
759 slice[9..13].copy_from_slice(&7u32.to_be_bytes());
760 slice[13..33].fill(0xAB);
761 33
762 });
763
764 let _guard = setup_mock(mock);
765
766 let amount = Amount::get_from_current_ledger_obj(sfield::Amount).unwrap();
767 match amount {
768 Amount::MPT {
769 num_units,
770 is_positive,
771 mpt_id,
772 } => {
773 assert_eq!(num_units, 100);
774 assert!(is_positive);
775 assert_eq!(mpt_id.get_sequence_num(), 7);
776 assert_eq!(mpt_id.get_issuer(), AccountID::from([0xAB; 20]));
777 }
778 _ => panic!("expected MPT variant"),
779 }
780 }
781
782 #[test]
783 fn test_amount_decodes_iou_variant() {
784 let mut mock = MockHostBindings::new();
785 mock.expect_get_current_ledger_obj_field()
786 .with(eq(sfield::Amount), always(), eq(48))
787 .times(1)
788 .returning(|_, buf, size| {
789 let slice = unsafe { core::slice::from_raw_parts_mut(buf, size) };
793 slice.fill(0);
794 slice[0] = 0x80;
795 slice[8..28].fill(0xCC);
796 slice[28..48].fill(0xDD);
797 48
798 });
799
800 let _guard = setup_mock(mock);
801
802 let amount = Amount::get_from_current_ledger_obj(sfield::Amount).unwrap();
803 match amount {
804 Amount::IOU {
805 issuer, currency, ..
806 } => {
807 assert_eq!(issuer, AccountID::from([0xDD; 20]));
808 assert_eq!(currency, Currency::from([0xCC; 20]));
809 }
810 _ => panic!("expected IOU variant"),
811 }
812 }
813 }
814}
815
816pub mod ledger_object {
817 use super::LedgerObjectFieldGetter;
818 use crate::host::Result;
819 use crate::sfield::SField;
820
821 #[inline]
845 pub fn get_field<T: LedgerObjectFieldGetter, const CODE: i32>(
846 register_num: i32,
847 field: SField<T, CODE>,
848 ) -> Result<T> {
849 T::get_from_ledger_obj(register_num, field)
850 }
851
852 #[inline]
866 pub fn get_field_optional<T: LedgerObjectFieldGetter, const CODE: i32>(
867 register_num: i32,
868 field: SField<T, CODE>,
869 ) -> Result<Option<T>> {
870 T::get_from_ledger_obj_optional(register_num, field)
871 }
872
873 #[cfg(test)]
874 mod tests {
875 use super::*;
876 use crate::core::types::account_id::{ACCOUNT_ID_SIZE, AccountID};
877 use crate::core::types::amount::{AMOUNT_SIZE, Amount};
878 use crate::core::types::blob::{Blob, PUBLIC_KEY_BLOB_SIZE, PublicKeyBlob};
879 use crate::core::types::currency::{CURRENCY_SIZE, Currency};
880 use crate::core::types::issue::Issue;
881 use crate::core::types::uint::{
882 HASH128_SIZE, HASH160_SIZE, HASH192_SIZE, HASH256_SIZE, Hash128, Hash160, Hash192,
883 Hash256,
884 };
885 use crate::host::host_bindings_trait::MockHostBindings;
886 use crate::host::setup_mock;
887 use crate::sfield::{self, SField};
888 use mockall::predicate::{always, eq};
889
890 fn expect_ledger_field<
897 T: LedgerObjectFieldGetter + Send + std::fmt::Debug + PartialEq + 'static,
898 const CODE: i32,
899 >(
900 mock: &mut MockHostBindings,
901 slot: i32,
902 field: SField<T, CODE>,
903 size: usize,
904 times: usize,
905 ) {
906 mock.expect_get_ledger_obj_field()
907 .with(eq(slot), eq(field), always(), eq(size))
908 .times(times)
909 .returning(move |_, _, buf, buf_size| {
910 unsafe { core::ptr::write_bytes(buf, 0, buf_size) };
911 size as i32
912 });
913 }
914
915 fn expect_ledger_field_short<
918 T: LedgerObjectFieldGetter + Send + std::fmt::Debug + PartialEq + 'static,
919 const CODE: i32,
920 >(
921 mock: &mut MockHostBindings,
922 slot: i32,
923 field: SField<T, CODE>,
924 buf_size: usize,
925 returned: i32,
926 ) {
927 mock.expect_get_ledger_obj_field()
928 .with(eq(slot), eq(field), always(), eq(buf_size))
929 .times(1)
930 .returning(move |_, _, buf, buf_size| {
931 unsafe { core::ptr::write_bytes(buf, 0, buf_size) };
932 returned
933 });
934 }
935
936 #[test]
937 fn test_ledger_basic_types() {
938 let mut mock = MockHostBindings::new();
939 let slot = 0;
940
941 expect_ledger_field(&mut mock, slot, sfield::LedgerEntryType, 2, 1);
942 expect_ledger_field(&mut mock, slot, sfield::Flags, 4, 1);
943 expect_ledger_field(&mut mock, slot, sfield::OwnerNode, 8, 1);
944
945 let _guard = setup_mock(mock);
946
947 assert!(u16::get_from_ledger_obj(slot, sfield::LedgerEntryType).is_ok());
948 assert!(u32::get_from_ledger_obj(slot, sfield::Flags).is_ok());
949 assert!(u64::get_from_ledger_obj(slot, sfield::OwnerNode).is_ok());
950 }
951
952 #[test]
953 fn test_ledger_xrpl_types() {
954 let mut mock = MockHostBindings::new();
955 let slot = 0;
956
957 expect_ledger_field(&mut mock, slot, sfield::Account, ACCOUNT_ID_SIZE, 1);
958 expect_ledger_field(&mut mock, slot, sfield::Amount, AMOUNT_SIZE, 1);
959 expect_ledger_field(&mut mock, slot, sfield::Balance, AMOUNT_SIZE, 1);
960 expect_ledger_field(&mut mock, slot, sfield::EmailHash, HASH128_SIZE, 1);
961 expect_ledger_field(&mut mock, slot, sfield::PreviousTxnID, HASH256_SIZE, 1);
962 expect_ledger_field(&mut mock, slot, sfield::PublicKey, PUBLIC_KEY_BLOB_SIZE, 1);
963 expect_ledger_field(&mut mock, slot, sfield::TakerPaysCurrency, HASH160_SIZE, 1);
964 expect_ledger_field(&mut mock, slot, sfield::MPTokenIssuanceID, HASH192_SIZE, 1);
965 expect_ledger_field(&mut mock, slot, sfield::BaseAsset, CURRENCY_SIZE, 1);
966 expect_ledger_field_short(&mut mock, slot, sfield::Asset, 40, 20);
967
968 let _guard = setup_mock(mock);
969
970 assert!(AccountID::get_from_ledger_obj(slot, sfield::Account).is_ok());
971 assert!(Amount::get_from_ledger_obj(slot, sfield::Amount).is_ok());
972 assert!(Amount::get_from_ledger_obj(slot, sfield::Balance).is_ok());
973 assert!(Hash128::get_from_ledger_obj(slot, sfield::EmailHash).is_ok());
974 assert!(Hash256::get_from_ledger_obj(slot, sfield::PreviousTxnID).is_ok());
975
976 let blob: PublicKeyBlob = Blob::get_from_ledger_obj(slot, sfield::PublicKey).unwrap();
977 assert_eq!(blob.len, PUBLIC_KEY_BLOB_SIZE);
978
979 assert!(Hash160::get_from_ledger_obj(slot, sfield::TakerPaysCurrency).is_ok());
980 assert!(Hash192::get_from_ledger_obj(slot, sfield::MPTokenIssuanceID).is_ok());
981 assert!(Currency::get_from_ledger_obj(slot, sfield::BaseAsset).is_ok());
982 assert!(Issue::get_from_ledger_obj(slot, sfield::Asset).is_ok());
983 }
984
985 #[test]
986 fn test_ledger_optional_fields() {
987 let mut mock = MockHostBindings::new();
988 let slot = 0;
989
990 expect_ledger_field(&mut mock, slot, sfield::SourceTag, 4, 1);
991 expect_ledger_field(&mut mock, slot, sfield::Destination, ACCOUNT_ID_SIZE, 1);
992 expect_ledger_field(&mut mock, slot, sfield::Amount, AMOUNT_SIZE, 1);
993 expect_ledger_field(&mut mock, slot, sfield::TakerPaysCurrency, HASH160_SIZE, 1);
994 expect_ledger_field(&mut mock, slot, sfield::MPTokenIssuanceID, HASH192_SIZE, 1);
995 expect_ledger_field(&mut mock, slot, sfield::BaseAsset, CURRENCY_SIZE, 1);
996 expect_ledger_field(&mut mock, slot, sfield::EmailHash, HASH128_SIZE, 1);
997 expect_ledger_field(&mut mock, slot, sfield::AccountTxnID, HASH256_SIZE, 1);
998 expect_ledger_field(&mut mock, slot, sfield::PublicKey, PUBLIC_KEY_BLOB_SIZE, 1);
999 expect_ledger_field_short(&mut mock, slot, sfield::Asset, 40, 20);
1000
1001 let _guard = setup_mock(mock);
1002
1003 let result = u32::get_from_ledger_obj_optional(slot, sfield::SourceTag);
1004 assert!(result.is_ok());
1005 assert!(result.unwrap().is_some());
1006
1007 let result = AccountID::get_from_ledger_obj_optional(slot, sfield::Destination);
1008 assert!(result.is_ok());
1009 assert!(result.unwrap().is_some());
1010
1011 let result = Amount::get_from_ledger_obj_optional(slot, sfield::Amount);
1012 assert!(result.is_ok());
1013 assert!(result.unwrap().is_some());
1014
1015 let result = Hash160::get_from_ledger_obj_optional(slot, sfield::TakerPaysCurrency);
1016 assert!(result.is_ok());
1017 assert!(result.unwrap().is_some());
1018
1019 let result = Hash192::get_from_ledger_obj_optional(slot, sfield::MPTokenIssuanceID);
1020 assert!(result.is_ok());
1021 assert!(result.unwrap().is_some());
1022
1023 let result = Currency::get_from_ledger_obj_optional(slot, sfield::BaseAsset);
1024 assert!(result.is_ok());
1025 assert!(result.unwrap().is_some());
1026
1027 let result = Hash128::get_from_ledger_obj_optional(slot, sfield::EmailHash);
1028 assert!(result.is_ok());
1029 assert!(result.unwrap().is_some());
1030
1031 let result = Hash256::get_from_ledger_obj_optional(slot, sfield::AccountTxnID);
1032 assert!(result.is_ok());
1033 assert!(result.unwrap().is_some());
1034
1035 let result = PublicKeyBlob::get_from_ledger_obj_optional(slot, sfield::PublicKey);
1036 assert!(result.is_ok());
1037 assert!(result.unwrap().is_some());
1038
1039 let result = Issue::get_from_ledger_obj_optional(slot, sfield::Asset);
1040 assert!(result.is_ok());
1041 assert!(result.unwrap().is_some());
1042 }
1043
1044 #[test]
1045 fn test_ledger_module_convenience_functions() {
1046 let mut mock = MockHostBindings::new();
1047 let slot = 0;
1048
1049 expect_ledger_field(&mut mock, slot, sfield::Flags, 4, 2);
1050 expect_ledger_field(&mut mock, slot, sfield::Account, ACCOUNT_ID_SIZE, 2);
1051 expect_ledger_field(&mut mock, slot, sfield::Balance, AMOUNT_SIZE, 1);
1052 expect_ledger_field(&mut mock, slot, sfield::PublicKey, PUBLIC_KEY_BLOB_SIZE, 1);
1053
1054 let _guard = setup_mock(mock);
1055
1056 assert!(get_field(slot, sfield::Flags).is_ok());
1057 assert!(get_field(slot, sfield::Account).is_ok());
1058 assert!(get_field(slot, sfield::Balance).is_ok());
1059 assert!(get_field(slot, sfield::PublicKey).is_ok());
1060
1061 let result = get_field_optional(slot, sfield::Flags);
1062 assert!(result.is_ok());
1063 assert!(result.unwrap().is_some());
1064
1065 let result = get_field_optional(slot, sfield::Account);
1066 assert!(result.is_ok());
1067 assert!(result.unwrap().is_some());
1068 }
1069
1070 #[test]
1071 fn test_type_inference() {
1072 let mut mock = MockHostBindings::new();
1073 let slot = 0;
1074
1075 expect_ledger_field(&mut mock, slot, sfield::Balance, AMOUNT_SIZE, 1);
1076 expect_ledger_field(&mut mock, slot, sfield::Account, ACCOUNT_ID_SIZE, 1);
1077 expect_ledger_field(&mut mock, slot, sfield::Sequence, 4, 1);
1078 expect_ledger_field(&mut mock, slot, sfield::Flags, 4, 1);
1079
1080 let _guard = setup_mock(mock);
1081
1082 let _balance = get_field(slot, sfield::Balance);
1083 let _account = get_field(slot, sfield::Account);
1084
1085 let _sequence: Result<u32> = get_field(slot, sfield::Sequence);
1086 let _flags: Result<u32> = get_field(slot, sfield::Flags);
1087 }
1088 }
1089}
1090
1091#[cfg(test)]
1092mod tests {
1093 use super::current_ledger_object;
1094 use super::ledger_object;
1095 use crate::sfield;
1096
1097 #[test]
1098 #[should_panic]
1099 fn test_array_get_field_panics() {
1100 let _ = current_ledger_object::get_field(sfield::Signers);
1101 }
1102
1103 #[test]
1104 #[should_panic]
1105 fn test_array_get_field_optional_panics() {
1106 let _ = current_ledger_object::get_field_optional(sfield::Signers);
1107 }
1108
1109 #[test]
1110 #[should_panic]
1111 fn test_array_get_field_with_slot_panics() {
1112 let _ = ledger_object::get_field(0, sfield::Signers);
1113 }
1114
1115 #[test]
1116 #[should_panic]
1117 fn test_array_get_field_optional_with_slot_panics() {
1118 let _ = ledger_object::get_field_optional(0, sfield::Signers);
1119 }
1120
1121 #[test]
1122 #[should_panic]
1123 fn test_object_get_field_panics() {
1124 let _ = current_ledger_object::get_field(sfield::Memo);
1125 }
1126
1127 #[test]
1128 #[should_panic]
1129 fn test_object_get_field_optional_panics() {
1130 let _ = current_ledger_object::get_field_optional(sfield::Memo);
1131 }
1132
1133 #[test]
1134 #[should_panic]
1135 fn test_object_get_field_with_slot_panics() {
1136 let _ = ledger_object::get_field(0, sfield::Memo);
1137 }
1138
1139 #[test]
1140 #[should_panic]
1141 fn test_object_get_field_optional_with_slot_panics() {
1142 let _ = ledger_object::get_field_optional(0, sfield::Memo);
1143 }
1144}