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};
12
13pub trait LedgerObjectFieldGetter: Sized {
62 fn get_from_current_ledger_obj(field_code: i32) -> Result<Self>;
74
75 fn get_from_current_ledger_obj_optional(field_code: i32) -> Result<Option<Self>>;
88
89 fn get_from_ledger_obj(register_num: i32, field_code: i32) -> Result<Self>;
102
103 fn get_from_ledger_obj_optional(register_num: i32, field_code: i32) -> Result<Option<Self>>;
117}
118
119trait FixedSizeFieldType: Sized {
132 const SIZE: usize;
134}
135
136impl FixedSizeFieldType for u8 {
137 const SIZE: usize = 1;
138}
139
140impl FixedSizeFieldType for u16 {
141 const SIZE: usize = 2;
142}
143
144impl FixedSizeFieldType for u32 {
145 const SIZE: usize = 4;
146}
147
148impl FixedSizeFieldType for u64 {
149 const SIZE: usize = 8;
150}
151
152impl<T: FixedSizeFieldType> LedgerObjectFieldGetter for T {
166 #[inline]
167 fn get_from_current_ledger_obj(field_code: i32) -> Result<Self> {
168 let mut value = core::mem::MaybeUninit::<T>::uninit();
169 let result_code =
170 unsafe { get_current_ledger_obj_field(field_code, value.as_mut_ptr().cast(), T::SIZE) };
171 match_result_code_with_expected_bytes(result_code, T::SIZE, || unsafe {
172 value.assume_init()
173 })
174 }
175
176 #[inline]
177 fn get_from_current_ledger_obj_optional(field_code: i32) -> Result<Option<Self>> {
178 let mut value = core::mem::MaybeUninit::<T>::uninit();
179 let result_code =
180 unsafe { get_current_ledger_obj_field(field_code, value.as_mut_ptr().cast(), T::SIZE) };
181 match_result_code_with_expected_bytes_optional(result_code, T::SIZE, || {
182 Some(unsafe { value.assume_init() })
183 })
184 }
185
186 #[inline]
187 fn get_from_ledger_obj(register_num: i32, field_code: i32) -> Result<Self> {
188 let mut value = core::mem::MaybeUninit::<T>::uninit();
189 let result_code = unsafe {
190 get_ledger_obj_field(register_num, field_code, value.as_mut_ptr().cast(), T::SIZE)
191 };
192 match_result_code_with_expected_bytes(result_code, T::SIZE, || unsafe {
193 value.assume_init()
194 })
195 }
196
197 #[inline]
198 fn get_from_ledger_obj_optional(register_num: i32, field_code: i32) -> Result<Option<Self>> {
199 let mut value = core::mem::MaybeUninit::<T>::uninit();
200 let result_code = unsafe {
201 get_ledger_obj_field(register_num, field_code, value.as_mut_ptr().cast(), T::SIZE)
202 };
203 match_result_code_with_expected_bytes_optional(result_code, T::SIZE, || {
204 Some(unsafe { value.assume_init() })
205 })
206 }
207}
208
209impl LedgerObjectFieldGetter for Hash160 {
219 #[inline]
220 fn get_from_current_ledger_obj(field_code: i32) -> Result<Self> {
221 let mut buffer = core::mem::MaybeUninit::<[u8; HASH160_SIZE]>::uninit();
222 let result_code = unsafe {
223 get_current_ledger_obj_field(field_code, buffer.as_mut_ptr().cast(), HASH160_SIZE)
224 };
225 match_result_code_with_expected_bytes(result_code, HASH160_SIZE, || {
226 Hash160::from(unsafe { buffer.assume_init() })
227 })
228 }
229
230 #[inline]
231 fn get_from_current_ledger_obj_optional(field_code: i32) -> Result<Option<Self>> {
232 let mut buffer = core::mem::MaybeUninit::<[u8; HASH160_SIZE]>::uninit();
233 let result_code = unsafe {
234 get_current_ledger_obj_field(field_code, buffer.as_mut_ptr().cast(), HASH160_SIZE)
235 };
236 match_result_code_with_expected_bytes_optional(result_code, HASH160_SIZE, || {
237 Some(Hash160::from(unsafe { buffer.assume_init() }))
238 })
239 }
240
241 #[inline]
242 fn get_from_ledger_obj(register_num: i32, field_code: i32) -> Result<Self> {
243 let mut buffer = core::mem::MaybeUninit::<[u8; HASH160_SIZE]>::uninit();
244 let result_code = unsafe {
245 get_ledger_obj_field(
246 register_num,
247 field_code,
248 buffer.as_mut_ptr().cast(),
249 HASH160_SIZE,
250 )
251 };
252 match_result_code_with_expected_bytes(result_code, HASH160_SIZE, || {
253 Hash160::from(unsafe { buffer.assume_init() })
254 })
255 }
256
257 #[inline]
258 fn get_from_ledger_obj_optional(register_num: i32, field_code: i32) -> Result<Option<Self>> {
259 let mut buffer = core::mem::MaybeUninit::<[u8; HASH160_SIZE]>::uninit();
260 let result_code = unsafe {
261 get_ledger_obj_field(
262 register_num,
263 field_code,
264 buffer.as_mut_ptr().cast(),
265 HASH160_SIZE,
266 )
267 };
268 match_result_code_with_expected_bytes_optional(result_code, HASH160_SIZE, || {
269 Some(Hash160::from(unsafe { buffer.assume_init() }))
270 })
271 }
272}
273
274impl LedgerObjectFieldGetter for Hash192 {
284 #[inline]
285 fn get_from_current_ledger_obj(field_code: i32) -> Result<Self> {
286 let mut buffer = core::mem::MaybeUninit::<[u8; HASH192_SIZE]>::uninit();
287 let result_code = unsafe {
288 get_current_ledger_obj_field(field_code, buffer.as_mut_ptr().cast(), HASH192_SIZE)
289 };
290 match_result_code_with_expected_bytes(result_code, HASH192_SIZE, || {
291 Hash192::from(unsafe { buffer.assume_init() })
292 })
293 }
294
295 #[inline]
296 fn get_from_current_ledger_obj_optional(field_code: i32) -> Result<Option<Self>> {
297 let mut buffer = core::mem::MaybeUninit::<[u8; HASH192_SIZE]>::uninit();
298 let result_code = unsafe {
299 get_current_ledger_obj_field(field_code, buffer.as_mut_ptr().cast(), HASH192_SIZE)
300 };
301 match_result_code_with_expected_bytes_optional(result_code, HASH192_SIZE, || {
302 Some(Hash192::from(unsafe { buffer.assume_init() }))
303 })
304 }
305
306 #[inline]
307 fn get_from_ledger_obj(register_num: i32, field_code: i32) -> Result<Self> {
308 let mut buffer = core::mem::MaybeUninit::<[u8; HASH192_SIZE]>::uninit();
309 let result_code = unsafe {
310 get_ledger_obj_field(
311 register_num,
312 field_code,
313 buffer.as_mut_ptr().cast(),
314 HASH192_SIZE,
315 )
316 };
317 match_result_code_with_expected_bytes(result_code, HASH192_SIZE, || {
318 Hash192::from(unsafe { buffer.assume_init() })
319 })
320 }
321
322 #[inline]
323 fn get_from_ledger_obj_optional(register_num: i32, field_code: i32) -> Result<Option<Self>> {
324 let mut buffer = core::mem::MaybeUninit::<[u8; HASH192_SIZE]>::uninit();
325 let result_code = unsafe {
326 get_ledger_obj_field(
327 register_num,
328 field_code,
329 buffer.as_mut_ptr().cast(),
330 HASH192_SIZE,
331 )
332 };
333 match_result_code_with_expected_bytes_optional(result_code, HASH192_SIZE, || {
334 Some(Hash192::from(unsafe { buffer.assume_init() }))
335 })
336 }
337}
338
339pub mod current_ledger_object {
340 use super::LedgerObjectFieldGetter;
341 use crate::host::Result;
342 use crate::sfield::SField;
343
344 #[inline]
367 pub fn get_field<T: LedgerObjectFieldGetter, const CODE: i32>(
368 _field: SField<T, CODE>,
369 ) -> Result<T> {
370 T::get_from_current_ledger_obj(CODE)
371 }
372
373 #[inline]
386 pub fn get_field_optional<T: LedgerObjectFieldGetter, const CODE: i32>(
387 _field: SField<T, CODE>,
388 ) -> Result<Option<T>> {
389 T::get_from_current_ledger_obj_optional(CODE)
390 }
391}
392
393pub mod ledger_object {
394 use super::LedgerObjectFieldGetter;
395 use crate::host::Result;
396 use crate::sfield::SField;
397
398 #[inline]
422 pub fn get_field<T: LedgerObjectFieldGetter, const CODE: i32>(
423 register_num: i32,
424 _field: SField<T, CODE>,
425 ) -> Result<T> {
426 T::get_from_ledger_obj(register_num, CODE)
427 }
428
429 #[inline]
443 pub fn get_field_optional<T: LedgerObjectFieldGetter, const CODE: i32>(
444 register_num: i32,
445 _field: SField<T, CODE>,
446 ) -> Result<Option<T>> {
447 T::get_from_ledger_obj_optional(register_num, CODE)
448 }
449
450 #[cfg(test)]
451 mod tests {
452 use super::*;
453 use crate::core::ledger_objects::{current_ledger_object, ledger_object};
454 use crate::core::types::account_id::{ACCOUNT_ID_SIZE, AccountID};
455 use crate::core::types::amount::{AMOUNT_SIZE, Amount};
456 use crate::core::types::blob::{Blob, DEFAULT_BLOB_SIZE};
457 use crate::core::types::currency::{CURRENCY_SIZE, Currency};
458 use crate::core::types::issue::Issue;
459 use crate::core::types::public_key::PUBLIC_KEY_BUFFER_SIZE;
460 use crate::core::types::uint::{
461 HASH128_SIZE, HASH160_SIZE, HASH192_SIZE, HASH256_SIZE, Hash128, Hash160, Hash192,
462 Hash256,
463 };
464 use crate::host::host_bindings_trait::MockHostBindings;
465 use crate::host::setup_mock;
466 use crate::sfield;
467 use mockall::predicate::{always, eq};
468
469 fn expect_current_field(
480 mock: &mut MockHostBindings,
481 field_code: i32,
482 size: usize,
483 times: usize,
484 ) {
485 mock.expect_get_current_ledger_obj_field()
486 .with(eq(field_code), always(), eq(size))
487 .times(times)
488 .returning(move |_, buf, buf_size| {
489 unsafe { core::ptr::write_bytes(buf, 0, buf_size) };
490 size as i32
491 });
492 }
493
494 fn expect_ledger_field(
497 mock: &mut MockHostBindings,
498 slot: i32,
499 field_code: i32,
500 size: usize,
501 times: usize,
502 ) {
503 mock.expect_get_ledger_obj_field()
504 .with(eq(slot), eq(field_code), always(), eq(size))
505 .times(times)
506 .returning(move |_, _, buf, buf_size| {
507 unsafe { core::ptr::write_bytes(buf, 0, buf_size) };
508 size as i32
509 });
510 }
511
512 fn expect_current_field_short(
516 mock: &mut MockHostBindings,
517 field_code: i32,
518 buf_size: usize,
519 returned: i32,
520 ) {
521 mock.expect_get_current_ledger_obj_field()
522 .with(eq(field_code), always(), eq(buf_size))
523 .times(1)
524 .returning(move |_, _, _| returned);
525 }
526
527 fn expect_ledger_field_short(
530 mock: &mut MockHostBindings,
531 slot: i32,
532 field_code: i32,
533 buf_size: usize,
534 returned: i32,
535 ) {
536 mock.expect_get_ledger_obj_field()
537 .with(eq(slot), eq(field_code), always(), eq(buf_size))
538 .times(1)
539 .returning(move |_, _, _, _| returned);
540 }
541
542 #[test]
549 fn test_field_getter_basic_types() {
550 let mut mock = MockHostBindings::new();
551
552 expect_current_field(&mut mock, sfield::LedgerEntryType.into(), 2, 1);
553 expect_current_field(&mut mock, sfield::Flags.into(), 4, 1);
554 expect_current_field(&mut mock, sfield::Balance.into(), 8, 1);
555
556 let _guard = setup_mock(mock);
557
558 assert!(u16::get_from_current_ledger_obj(sfield::LedgerEntryType.into()).is_ok());
560 assert!(u32::get_from_current_ledger_obj(sfield::Flags.into()).is_ok());
561 assert!(u64::get_from_current_ledger_obj(sfield::Balance.into()).is_ok());
562 }
563
564 #[test]
565 fn test_field_getter_xrpl_types() {
566 let mut mock = MockHostBindings::new();
567
568 expect_current_field(&mut mock, sfield::Account.into(), ACCOUNT_ID_SIZE, 1);
569 expect_current_field(&mut mock, sfield::Amount.into(), AMOUNT_SIZE, 1);
570 expect_current_field(&mut mock, sfield::EmailHash.into(), HASH128_SIZE, 1);
571 expect_current_field(&mut mock, sfield::PreviousTxnID.into(), HASH256_SIZE, 1);
572 expect_current_field(&mut mock, sfield::PublicKey.into(), DEFAULT_BLOB_SIZE, 1);
573 expect_current_field(&mut mock, sfield::TakerPaysCurrency.into(), HASH160_SIZE, 1);
574 expect_current_field(&mut mock, sfield::MPTokenIssuanceID.into(), HASH192_SIZE, 1);
575 expect_current_field(&mut mock, sfield::BaseAsset.into(), CURRENCY_SIZE, 1);
576 expect_current_field_short(&mut mock, sfield::Asset.into(), 40, 20);
577
578 let _guard = setup_mock(mock);
579
580 assert!(AccountID::get_from_current_ledger_obj(sfield::Account.into()).is_ok());
582 assert!(Amount::get_from_current_ledger_obj(sfield::Amount.into()).is_ok());
583 assert!(Hash128::get_from_current_ledger_obj(sfield::EmailHash.into()).is_ok());
584 assert!(Hash256::get_from_current_ledger_obj(sfield::PreviousTxnID.into()).is_ok());
585 assert!(Hash160::get_from_current_ledger_obj(sfield::TakerPaysCurrency.into()).is_ok());
586 assert!(Hash192::get_from_current_ledger_obj(sfield::MPTokenIssuanceID.into()).is_ok());
587 assert!(Currency::get_from_current_ledger_obj(sfield::BaseAsset.into()).is_ok());
588 assert!(Issue::get_from_current_ledger_obj(sfield::Asset.into()).is_ok());
589
590 let blob: Blob<DEFAULT_BLOB_SIZE> =
591 Blob::get_from_current_ledger_obj(sfield::PublicKey.into()).unwrap();
592 assert_eq!(blob.len, DEFAULT_BLOB_SIZE);
594 }
595
596 #[test]
597 fn test_field_getter_optional_variants() {
598 let mut mock = MockHostBindings::new();
599
600 expect_current_field(&mut mock, sfield::Flags.into(), 4, 1);
601 expect_current_field(&mut mock, sfield::Account.into(), ACCOUNT_ID_SIZE, 1);
602 expect_current_field(&mut mock, sfield::TakerPaysCurrency.into(), HASH160_SIZE, 1);
603 expect_current_field(&mut mock, sfield::MPTokenIssuanceID.into(), HASH192_SIZE, 1);
604 expect_current_field(&mut mock, sfield::Amount.into(), AMOUNT_SIZE, 1);
605 expect_current_field(&mut mock, sfield::BaseAsset.into(), CURRENCY_SIZE, 1);
606 expect_current_field(&mut mock, sfield::EmailHash.into(), HASH128_SIZE, 1);
607 expect_current_field(&mut mock, sfield::PreviousTxnID.into(), HASH256_SIZE, 1);
608 expect_current_field(&mut mock, sfield::PublicKey.into(), DEFAULT_BLOB_SIZE, 1);
609 expect_current_field_short(&mut mock, sfield::Asset.into(), 40, 20);
610
611 let _guard = setup_mock(mock);
612
613 let result = u32::get_from_current_ledger_obj_optional(sfield::Flags.into());
615 assert!(result.is_ok());
616 assert!(result.unwrap().is_some());
617
618 let result = AccountID::get_from_current_ledger_obj_optional(sfield::Account.into());
619 assert!(result.is_ok());
620 assert!(result.unwrap().is_some());
621
622 let result =
623 Hash160::get_from_current_ledger_obj_optional(sfield::TakerPaysCurrency.into());
624 assert!(result.is_ok());
625 assert!(result.unwrap().is_some());
626
627 let result =
628 Hash192::get_from_current_ledger_obj_optional(sfield::MPTokenIssuanceID.into());
629 assert!(result.is_ok());
630 assert!(result.unwrap().is_some());
631
632 let result = Amount::get_from_current_ledger_obj_optional(sfield::Amount.into());
633 assert!(result.is_ok());
634 assert!(result.unwrap().is_some());
635
636 let result = Currency::get_from_current_ledger_obj_optional(sfield::BaseAsset.into());
637 assert!(result.is_ok());
638 assert!(result.unwrap().is_some());
639
640 let result = Hash128::get_from_current_ledger_obj_optional(sfield::EmailHash.into());
641 assert!(result.is_ok());
642 assert!(result.unwrap().is_some());
643
644 let result =
645 Hash256::get_from_current_ledger_obj_optional(sfield::PreviousTxnID.into());
646 assert!(result.is_ok());
647 assert!(result.unwrap().is_some());
648
649 let result = Blob::<DEFAULT_BLOB_SIZE>::get_from_current_ledger_obj_optional(
650 sfield::PublicKey.into(),
651 );
652 assert!(result.is_ok());
653 assert!(result.unwrap().is_some());
654
655 let result = Issue::get_from_current_ledger_obj_optional(sfield::Asset.into());
656 assert!(result.is_ok());
657 assert!(result.unwrap().is_some());
658 }
659
660 #[test]
661 fn test_field_getter_with_slot() {
662 let mut mock = MockHostBindings::new();
663 let slot = 0;
664
665 expect_ledger_field(&mut mock, slot, sfield::Flags.into(), 4, 1);
666 expect_ledger_field(&mut mock, slot, sfield::Balance.into(), 8, 1);
667 expect_ledger_field(&mut mock, slot, sfield::Account.into(), ACCOUNT_ID_SIZE, 1);
668 expect_ledger_field(
669 &mut mock,
670 slot,
671 sfield::TakerPaysCurrency.into(),
672 HASH160_SIZE,
673 1,
674 );
675 expect_ledger_field(
676 &mut mock,
677 slot,
678 sfield::MPTokenIssuanceID.into(),
679 HASH192_SIZE,
680 1,
681 );
682 expect_ledger_field(&mut mock, slot, sfield::Amount.into(), AMOUNT_SIZE, 1);
683 expect_ledger_field(&mut mock, slot, sfield::BaseAsset.into(), CURRENCY_SIZE, 1);
684 expect_ledger_field(&mut mock, slot, sfield::EmailHash.into(), HASH128_SIZE, 1);
685 expect_ledger_field(
686 &mut mock,
687 slot,
688 sfield::PreviousTxnID.into(),
689 HASH256_SIZE,
690 1,
691 );
692 expect_ledger_field(
693 &mut mock,
694 slot,
695 sfield::PublicKey.into(),
696 DEFAULT_BLOB_SIZE,
697 1,
698 );
699 expect_ledger_field_short(&mut mock, slot, sfield::Asset.into(), 40, 20);
700
701 let _guard = setup_mock(mock);
702
703 assert!(u32::get_from_ledger_obj(slot, sfield::Flags.into()).is_ok());
705 assert!(u64::get_from_ledger_obj(slot, sfield::Balance.into()).is_ok());
706 assert!(AccountID::get_from_ledger_obj(slot, sfield::Account.into()).is_ok());
707 assert!(Hash160::get_from_ledger_obj(slot, sfield::TakerPaysCurrency.into()).is_ok());
708 assert!(Hash192::get_from_ledger_obj(slot, sfield::MPTokenIssuanceID.into()).is_ok());
709 assert!(Amount::get_from_ledger_obj(slot, sfield::Amount.into()).is_ok());
710 assert!(Currency::get_from_ledger_obj(slot, sfield::BaseAsset.into()).is_ok());
711 assert!(Hash128::get_from_ledger_obj(slot, sfield::EmailHash.into()).is_ok());
712 assert!(Hash256::get_from_ledger_obj(slot, sfield::PreviousTxnID.into()).is_ok());
713 assert!(
714 Blob::<DEFAULT_BLOB_SIZE>::get_from_ledger_obj(slot, sfield::PublicKey.into())
715 .is_ok()
716 );
717 assert!(Issue::get_from_ledger_obj(slot, sfield::Asset.into()).is_ok());
718 }
719
720 #[test]
721 fn test_field_getter_optional_with_slot() {
722 let mut mock = MockHostBindings::new();
723 let slot = 0;
724
725 expect_ledger_field(&mut mock, slot, sfield::Flags.into(), 4, 1);
726 expect_ledger_field(
727 &mut mock,
728 slot,
729 sfield::TakerPaysCurrency.into(),
730 HASH160_SIZE,
731 1,
732 );
733 expect_ledger_field(
734 &mut mock,
735 slot,
736 sfield::MPTokenIssuanceID.into(),
737 HASH192_SIZE,
738 1,
739 );
740 expect_ledger_field(&mut mock, slot, sfield::Amount.into(), AMOUNT_SIZE, 1);
741 expect_ledger_field(&mut mock, slot, sfield::BaseAsset.into(), CURRENCY_SIZE, 1);
742 expect_ledger_field(&mut mock, slot, sfield::EmailHash.into(), HASH128_SIZE, 1);
743 expect_ledger_field(
744 &mut mock,
745 slot,
746 sfield::PreviousTxnID.into(),
747 HASH256_SIZE,
748 1,
749 );
750 expect_ledger_field(
751 &mut mock,
752 slot,
753 sfield::PublicKey.into(),
754 DEFAULT_BLOB_SIZE,
755 1,
756 );
757 expect_ledger_field_short(&mut mock, slot, sfield::Asset.into(), 40, 20);
758
759 let _guard = setup_mock(mock);
760
761 let result = u32::get_from_ledger_obj_optional(slot, sfield::Flags.into());
763 assert!(result.is_ok());
764 assert!(result.unwrap().is_some());
765
766 let result =
767 Hash160::get_from_ledger_obj_optional(slot, sfield::TakerPaysCurrency.into());
768 assert!(result.is_ok());
769 assert!(result.unwrap().is_some());
770
771 let result =
772 Hash192::get_from_ledger_obj_optional(slot, sfield::MPTokenIssuanceID.into());
773 assert!(result.is_ok());
774 assert!(result.unwrap().is_some());
775 let result = Amount::get_from_ledger_obj_optional(slot, sfield::Amount.into());
776 assert!(result.is_ok());
777 assert!(result.unwrap().is_some());
778
779 let result = Currency::get_from_ledger_obj_optional(slot, sfield::BaseAsset.into());
780 assert!(result.is_ok());
781 assert!(result.unwrap().is_some());
782
783 let result = Hash128::get_from_ledger_obj_optional(slot, sfield::EmailHash.into());
784 assert!(result.is_ok());
785 assert!(result.unwrap().is_some());
786
787 let result = Hash256::get_from_ledger_obj_optional(slot, sfield::PreviousTxnID.into());
788 assert!(result.is_ok());
789 assert!(result.unwrap().is_some());
790
791 let result = Blob::<DEFAULT_BLOB_SIZE>::get_from_ledger_obj_optional(
792 slot,
793 sfield::PublicKey.into(),
794 );
795 assert!(result.is_ok());
796 assert!(result.unwrap().is_some());
797
798 let result = Issue::get_from_ledger_obj_optional(slot, sfield::Asset.into());
799 assert!(result.is_ok());
800 assert!(result.unwrap().is_some());
801 }
802
803 #[test]
809 fn test_issue_decodes_xrp_variant() {
810 let mut mock = MockHostBindings::new();
811 mock.expect_get_current_ledger_obj_field()
812 .with(eq::<i32>(sfield::Asset.into()), always(), eq(40))
813 .times(1)
814 .returning(|_, buf, _| {
815 unsafe { core::ptr::write_bytes(buf, 0, 20) };
817 20
818 });
819
820 let _guard = setup_mock(mock);
821
822 let issue = Issue::get_from_current_ledger_obj(sfield::Asset.into()).unwrap();
823 assert!(matches!(issue, Issue::XRP(_)));
824 }
825
826 #[test]
827 fn test_issue_decodes_mpt_variant() {
828 let mut mock = MockHostBindings::new();
829 mock.expect_get_current_ledger_obj_field()
830 .with(eq::<i32>(sfield::Asset.into()), always(), eq(40))
831 .times(1)
832 .returning(|_, buf, _| {
833 let slice = unsafe { core::slice::from_raw_parts_mut(buf, 24) };
835 slice[0..4].copy_from_slice(&42u32.to_be_bytes());
836 slice[4..24].fill(0xAB);
837 24
838 });
839
840 let _guard = setup_mock(mock);
841
842 let issue = Issue::get_from_current_ledger_obj(sfield::Asset.into()).unwrap();
843 match issue {
844 Issue::MPT(mpt) => {
845 assert_eq!(mpt.mpt_id().get_sequence_num(), 42);
846 assert_eq!(mpt.mpt_id().get_issuer(), AccountID::from([0xAB; 20]));
847 }
848 _ => panic!("expected MPT variant"),
849 }
850 }
851
852 #[test]
853 fn test_issue_decodes_iou_variant() {
854 let mut mock = MockHostBindings::new();
855 mock.expect_get_current_ledger_obj_field()
856 .with(eq::<i32>(sfield::Asset.into()), always(), eq(40))
857 .times(1)
858 .returning(|_, buf, _| {
859 let slice = unsafe { core::slice::from_raw_parts_mut(buf, 40) };
861 slice[0..20].fill(0xCC);
862 slice[20..40].fill(0xDD);
863 40
864 });
865
866 let _guard = setup_mock(mock);
867
868 let issue = Issue::get_from_current_ledger_obj(sfield::Asset.into()).unwrap();
869 match issue {
870 Issue::IOU(iou) => {
871 let bytes = iou.as_bytes();
872 assert_eq!(&bytes[..20], &[0xCC; 20]);
873 assert_eq!(&bytes[20..], &[0xDD; 20]);
874 }
875 _ => panic!("expected IOU variant"),
876 }
877 }
878
879 #[test]
888 fn test_amount_decodes_xrp_variant() {
889 let mut mock = MockHostBindings::new();
890 mock.expect_get_current_ledger_obj_field()
891 .with(eq::<i32>(sfield::Amount.into()), always(), eq(48))
892 .times(1)
893 .returning(|_, buf, size| {
894 let slice = unsafe { core::slice::from_raw_parts_mut(buf, size) };
897 slice.fill(0);
898 let mut be = 1000u64.to_be_bytes();
899 be[0] |= 0x40; slice[0..8].copy_from_slice(&be);
901 8
902 });
903
904 let _guard = setup_mock(mock);
905
906 let amount = Amount::get_from_current_ledger_obj(sfield::Amount.into()).unwrap();
907 assert!(matches!(amount, Amount::XRP { num_drops: 1000 }));
908 }
909
910 #[test]
911 fn test_amount_decodes_mpt_variant() {
912 let mut mock = MockHostBindings::new();
913 mock.expect_get_current_ledger_obj_field()
914 .with(eq::<i32>(sfield::Amount.into()), always(), eq(48))
915 .times(1)
916 .returning(|_, buf, size| {
917 let slice = unsafe { core::slice::from_raw_parts_mut(buf, size) };
921 slice.fill(0);
922 slice[0] = 0x60;
923 slice[1..9].copy_from_slice(&100u64.to_be_bytes());
924 slice[9..13].copy_from_slice(&7u32.to_be_bytes());
925 slice[13..33].fill(0xAB);
926 33
927 });
928
929 let _guard = setup_mock(mock);
930
931 let amount = Amount::get_from_current_ledger_obj(sfield::Amount.into()).unwrap();
932 match amount {
933 Amount::MPT {
934 num_units,
935 is_positive,
936 mpt_id,
937 } => {
938 assert_eq!(num_units, 100);
939 assert!(is_positive);
940 assert_eq!(mpt_id.get_sequence_num(), 7);
941 assert_eq!(mpt_id.get_issuer(), AccountID::from([0xAB; 20]));
942 }
943 _ => panic!("expected MPT variant"),
944 }
945 }
946
947 #[test]
948 fn test_amount_decodes_iou_variant() {
949 let mut mock = MockHostBindings::new();
950 mock.expect_get_current_ledger_obj_field()
951 .with(eq::<i32>(sfield::Amount.into()), always(), eq(48))
952 .times(1)
953 .returning(|_, buf, size| {
954 let slice = unsafe { core::slice::from_raw_parts_mut(buf, size) };
958 slice.fill(0);
959 slice[0] = 0x80;
960 slice[8..28].fill(0xCC);
961 slice[28..48].fill(0xDD);
962 48
963 });
964
965 let _guard = setup_mock(mock);
966
967 let amount = Amount::get_from_current_ledger_obj(sfield::Amount.into()).unwrap();
968 match amount {
969 Amount::IOU {
970 issuer, currency, ..
971 } => {
972 assert_eq!(issuer, AccountID::from([0xDD; 20]));
973 assert_eq!(currency, Currency::from([0xCC; 20]));
974 }
975 _ => panic!("expected IOU variant"),
976 }
977 }
978
979 #[test]
984 fn test_current_ledger_object_module() {
985 let mut mock = MockHostBindings::new();
986
987 expect_current_field(&mut mock, sfield::Flags.into(), 4, 2);
988 expect_current_field(&mut mock, sfield::Account.into(), ACCOUNT_ID_SIZE, 1);
989
990 let _guard = setup_mock(mock);
991
992 assert!(current_ledger_object::get_field(sfield::Flags).is_ok());
994 assert!(current_ledger_object::get_field(sfield::Account).is_ok());
995
996 let result = current_ledger_object::get_field_optional(sfield::Flags);
997 assert!(result.is_ok());
998 assert!(result.unwrap().is_some());
999 }
1000
1001 #[test]
1002 fn test_ledger_object_module() {
1003 let mut mock = MockHostBindings::new();
1004 let slot = 0;
1005
1006 expect_ledger_field(&mut mock, slot, sfield::LedgerEntryType.into(), 2, 1);
1007 expect_ledger_field(&mut mock, slot, sfield::Flags.into(), 4, 2);
1008 expect_ledger_field(&mut mock, slot, sfield::Balance.into(), AMOUNT_SIZE, 1);
1009 expect_ledger_field(&mut mock, slot, sfield::Account.into(), ACCOUNT_ID_SIZE, 1);
1010 expect_ledger_field(&mut mock, slot, sfield::Amount.into(), AMOUNT_SIZE, 1);
1011 expect_ledger_field(&mut mock, slot, sfield::EmailHash.into(), HASH128_SIZE, 1);
1012 expect_ledger_field(
1013 &mut mock,
1014 slot,
1015 sfield::PreviousTxnID.into(),
1016 HASH256_SIZE,
1017 1,
1018 );
1019 expect_ledger_field(
1020 &mut mock,
1021 slot,
1022 sfield::PublicKey.into(),
1023 DEFAULT_BLOB_SIZE,
1024 1,
1025 );
1026
1027 let _guard = setup_mock(mock);
1028
1029 assert!(ledger_object::get_field(slot, sfield::LedgerEntryType).is_ok());
1031 assert!(ledger_object::get_field(slot, sfield::Flags).is_ok());
1032 assert!(ledger_object::get_field(slot, sfield::Balance).is_ok());
1033 assert!(ledger_object::get_field(slot, sfield::Account).is_ok());
1034 assert!(ledger_object::get_field(slot, sfield::Amount).is_ok());
1035 assert!(ledger_object::get_field(slot, sfield::EmailHash).is_ok());
1036 assert!(ledger_object::get_field(slot, sfield::PreviousTxnID).is_ok());
1037 assert!(ledger_object::get_field(slot, sfield::PublicKey).is_ok());
1038
1039 let result = ledger_object::get_field_optional(slot, sfield::Flags);
1040 assert!(result.is_ok());
1041 assert!(result.unwrap().is_some());
1042 }
1043
1044 #[test]
1049 fn test_type_inference() {
1050 let mut mock = MockHostBindings::new();
1051 let slot = 0;
1052
1053 expect_ledger_field(&mut mock, slot, sfield::Balance.into(), AMOUNT_SIZE, 1);
1054 expect_ledger_field(&mut mock, slot, sfield::Account.into(), ACCOUNT_ID_SIZE, 1);
1055 expect_ledger_field(&mut mock, slot, sfield::Sequence.into(), 4, 1);
1056 expect_ledger_field(&mut mock, slot, sfield::Flags.into(), 4, 1);
1057
1058 let _guard = setup_mock(mock);
1059
1060 let _balance = get_field(slot, sfield::Balance);
1062 let _account = get_field(slot, sfield::Account);
1063
1064 let _sequence: Result<u32> = get_field(slot, sfield::Sequence);
1066 let _flags: Result<u32> = get_field(slot, sfield::Flags);
1067 }
1068
1069 #[test]
1074 fn test_type_sizes() {
1075 let mut mock = MockHostBindings::new();
1076
1077 expect_current_field(&mut mock, sfield::EmailHash.into(), HASH128_SIZE, 1);
1078 expect_current_field(&mut mock, sfield::PreviousTxnID.into(), HASH256_SIZE, 1);
1079 expect_current_field(&mut mock, sfield::Account.into(), ACCOUNT_ID_SIZE, 1);
1080 expect_current_field(
1081 &mut mock,
1082 sfield::PublicKey.into(),
1083 PUBLIC_KEY_BUFFER_SIZE,
1084 1,
1085 );
1086
1087 let _guard = setup_mock(mock);
1088
1089 let hash128 = Hash128::get_from_current_ledger_obj(sfield::EmailHash.into()).unwrap();
1091 assert_eq!(hash128.as_bytes().len(), HASH128_SIZE);
1092
1093 let hash256 =
1094 Hash256::get_from_current_ledger_obj(sfield::PreviousTxnID.into()).unwrap();
1095 assert_eq!(hash256.as_bytes().len(), HASH256_SIZE);
1096
1097 let account = AccountID::get_from_current_ledger_obj(sfield::Account.into()).unwrap();
1098 assert_eq!(account.0.len(), ACCOUNT_ID_SIZE);
1099
1100 let blob: Blob<{ PUBLIC_KEY_BUFFER_SIZE }> =
1101 Blob::get_from_current_ledger_obj(sfield::PublicKey.into()).unwrap();
1102 assert_eq!(blob.len, PUBLIC_KEY_BUFFER_SIZE);
1104 assert_eq!(blob.data.len(), PUBLIC_KEY_BUFFER_SIZE);
1105 }
1106
1107 #[test]
1115 #[should_panic]
1116 fn test_array_get_field_panics() {
1117 let _ = current_ledger_object::get_field(sfield::Signers);
1118 }
1119
1120 #[test]
1121 #[should_panic]
1122 fn test_array_get_field_optional_panics() {
1123 let _ = current_ledger_object::get_field_optional(sfield::Signers);
1124 }
1125
1126 #[test]
1127 #[should_panic]
1128 fn test_array_get_field_with_slot_panics() {
1129 let _ = ledger_object::get_field(0, sfield::Signers);
1130 }
1131
1132 #[test]
1133 #[should_panic]
1134 fn test_array_get_field_optional_with_slot_panics() {
1135 let _ = ledger_object::get_field_optional(0, sfield::Signers);
1136 }
1137
1138 #[test]
1139 #[should_panic]
1140 fn test_object_get_field_panics() {
1141 let _ = current_ledger_object::get_field(sfield::Memo);
1142 }
1143
1144 #[test]
1145 #[should_panic]
1146 fn test_object_get_field_optional_panics() {
1147 let _ = current_ledger_object::get_field_optional(sfield::Memo);
1148 }
1149
1150 #[test]
1151 #[should_panic]
1152 fn test_object_get_field_with_slot_panics() {
1153 let _ = ledger_object::get_field(0, sfield::Memo);
1154 }
1155
1156 #[test]
1157 #[should_panic]
1158 fn test_object_get_field_optional_with_slot_panics() {
1159 let _ = ledger_object::get_field_optional(0, sfield::Memo);
1160 }
1161 }
1162}