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