1use crate::fields::decoder::{FromCurrentTx, FromLedger, decode_host_result};
39use crate::host::error_codes::match_result_code;
40use crate::host::{
41 self, Result, home_le_inner, home_le_inner_arr_len, le_inner, le_inner_arr_len, tx_inner,
42 tx_inner_arr_len,
43};
44use crate::sfield::SField;
45
46const LOCATOR_BUFFER_SIZE: usize = 64; #[derive(Clone, PartialEq, Eq, Debug)]
62#[repr(C)]
63pub struct Locator {
64 buffer: [u8; LOCATOR_BUFFER_SIZE],
65
66 cur_buffer_index: usize,
68}
69
70impl Default for Locator {
71 fn default() -> Self {
72 Self::new()
73 }
74}
75
76impl Locator {
77 pub fn new() -> Locator {
79 Self {
80 buffer: [0; LOCATOR_BUFFER_SIZE],
81 cur_buffer_index: 0,
82 }
83 }
84
85 pub fn pack(&mut self, sfield_or_index: impl Into<i32>) -> bool {
86 self.pack_value(sfield_or_index.into())
88 }
89
90 fn pack_value(&mut self, value: i32) -> bool {
91 if self.cur_buffer_index + 4 > LOCATOR_BUFFER_SIZE {
92 return false;
93 }
94
95 let value_bytes: [u8; 4] = value.to_le_bytes();
96 self.buffer[self.cur_buffer_index..self.cur_buffer_index + 4].copy_from_slice(&value_bytes);
97 self.cur_buffer_index += 4;
98
99 true
100 }
101
102 pub fn as_ptr(&self) -> *const u8 {
103 self.buffer.as_ptr()
104 }
105
106 pub fn num_packed_bytes(&self) -> usize {
107 self.cur_buffer_index
108 }
109
110 pub fn len(&self) -> usize {
111 self.cur_buffer_index
112 }
113
114 pub fn is_empty(&self) -> bool {
115 self.cur_buffer_index == 0
116 }
117
118 pub fn repack_last(&mut self, sfield_or_index: impl Into<i32>) -> bool {
119 self.repack_last_value(sfield_or_index.into())
120 }
121
122 fn repack_last_value(&mut self, value: i32) -> bool {
123 if self.cur_buffer_index < 4 {
124 return false;
125 }
126
127 self.cur_buffer_index -= 4;
128
129 let value_bytes: [u8; 4] = value.to_le_bytes();
130 self.buffer[self.cur_buffer_index..self.cur_buffer_index + 4].copy_from_slice(&value_bytes);
131 self.cur_buffer_index += 4;
132
133 true
134 }
135}
136
137fn array_len_for(
144 overflowed: bool,
145 locator: &Locator,
146 read: impl FnOnce(*const u8, usize) -> i32,
147) -> Result<u32> {
148 if overflowed {
149 return Result::Err(host::Error::LocatorMalformed);
150 }
151 let n = read(locator.as_ptr(), locator.num_packed_bytes());
152 match_result_code(n, || n as u32)
153}
154
155#[derive(Clone, PartialEq, Eq, Debug)]
190pub struct TxPathBuilder {
191 locator: Locator,
192 overflowed: bool,
196}
197
198impl TxPathBuilder {
199 pub(crate) fn for_current_tx() -> Self {
202 Self {
203 locator: Locator::new(),
204 overflowed: false,
205 }
206 }
207
208 pub fn field<T, const CODE: i32>(self, _field: SField<T, CODE>) -> Self {
214 self.push(CODE)
215 }
216
217 pub fn index(mut self, index: u32) -> Self {
225 match i32::try_from(index) {
226 Ok(index) => self.push(index),
227 Err(_) => {
228 self.overflowed = true;
229 self
230 }
231 }
232 }
233
234 fn push(mut self, value: i32) -> Self {
237 if !self.locator.pack(value) {
238 self.overflowed = true;
239 }
240 self
241 }
242
243 pub fn get<T: FromCurrentTx>(&self) -> Result<T> {
251 if self.overflowed {
252 return Result::Err(host::Error::LocatorMalformed);
253 }
254 let (buf, n) = self.read::<T>();
255 decode_host_result::<T>(buf, n)
256 }
257
258 pub fn get_optional<T: FromCurrentTx>(&self) -> Result<Option<T>> {
264 match self.get::<T>() {
265 Result::Ok(value) => Result::Ok(Some(value)),
266 Result::Err(host::Error::FieldNotFound) => Result::Ok(None),
267 Result::Err(e) => Result::Err(e),
268 }
269 }
270
271 pub fn array_len(&self) -> Result<u32> {
281 array_len_for(self.overflowed, &self.locator, |loc_ptr, loc_len| unsafe {
282 tx_inner_arr_len(loc_ptr, loc_len)
283 })
284 }
285
286 fn read<T: FromCurrentTx>(&self) -> (T::Buffer, i32) {
289 let mut buf = T::empty_buffer();
290 let n = {
291 let slice = buf.as_mut();
292 unsafe {
293 tx_inner(
294 self.locator.as_ptr(),
295 self.locator.num_packed_bytes(),
296 slice.as_mut_ptr(),
297 slice.len(),
298 )
299 }
300 };
301 (buf, n)
302 }
303}
304
305#[derive(Clone, PartialEq, Eq, Debug)]
313enum LedgerSource {
314 Current,
316 Slot(i32),
318}
319
320impl LedgerSource {
321 fn read_field(
323 &self,
324 loc_ptr: *const u8,
325 loc_len: usize,
326 out_ptr: *mut u8,
327 out_len: usize,
328 ) -> i32 {
329 match *self {
330 LedgerSource::Current => unsafe { home_le_inner(loc_ptr, loc_len, out_ptr, out_len) },
331 LedgerSource::Slot(slot) => unsafe {
332 le_inner(slot, loc_ptr, loc_len, out_ptr, out_len)
333 },
334 }
335 }
336
337 fn read_array_len(&self, loc_ptr: *const u8, loc_len: usize) -> i32 {
339 match *self {
340 LedgerSource::Current => unsafe { home_le_inner_arr_len(loc_ptr, loc_len) },
341 LedgerSource::Slot(slot) => unsafe { le_inner_arr_len(slot, loc_ptr, loc_len) },
342 }
343 }
344}
345
346#[derive(Clone, PartialEq, Eq, Debug)]
381pub struct LedgerPathBuilder {
382 locator: Locator,
383 overflowed: bool,
387 source: LedgerSource,
388}
389
390impl LedgerPathBuilder {
391 pub(crate) fn for_current_ledger_obj() -> Self {
394 Self::new(LedgerSource::Current)
395 }
396
397 pub(crate) fn for_ledger_obj(slot: i32) -> Self {
400 Self::new(LedgerSource::Slot(slot))
401 }
402
403 fn new(source: LedgerSource) -> Self {
404 Self {
405 locator: Locator::new(),
406 overflowed: false,
407 source,
408 }
409 }
410
411 pub fn field<T, const CODE: i32>(self, _field: SField<T, CODE>) -> Self {
413 self.push(CODE)
414 }
415
416 pub fn index(mut self, index: u32) -> Self {
419 match i32::try_from(index) {
420 Ok(index) => self.push(index),
421 Err(_) => {
422 self.overflowed = true;
423 self
424 }
425 }
426 }
427
428 fn push(mut self, value: i32) -> Self {
430 if !self.locator.pack(value) {
431 self.overflowed = true;
432 }
433 self
434 }
435
436 pub fn get<T: FromLedger>(&self) -> Result<T> {
441 if self.overflowed {
442 return Result::Err(host::Error::LocatorMalformed);
443 }
444 let (buf, n) = self.read::<T>();
445 decode_host_result::<T>(buf, n)
446 }
447
448 pub fn get_optional<T: FromLedger>(&self) -> Result<Option<T>> {
458 match self.get::<T>() {
459 Result::Ok(value) => Result::Ok(Some(value)),
460 Result::Err(host::Error::FieldNotFound) => Result::Ok(None),
461 Result::Err(e) => Result::Err(e),
462 }
463 }
464
465 pub fn array_len(&self) -> Result<u32> {
472 array_len_for(self.overflowed, &self.locator, |loc_ptr, loc_len| {
473 self.source.read_array_len(loc_ptr, loc_len)
474 })
475 }
476
477 fn read<T: FromLedger>(&self) -> (T::Buffer, i32) {
480 let mut buf = T::empty_buffer();
481 let n = {
482 let slice = buf.as_mut();
483 self.source.read_field(
484 self.locator.as_ptr(),
485 self.locator.num_packed_bytes(),
486 slice.as_mut_ptr(),
487 slice.len(),
488 )
489 };
490 (buf, n)
491 }
492}
493
494#[cfg(test)]
495mod tests {
496 use super::*;
497 use crate::sfield;
498
499 #[test]
500 fn test_pack_with_sfield_no_into_needed() {
501 let mut locator = Locator::new();
503
504 assert!(locator.pack(sfield::Memos));
506 assert!(locator.pack(0));
507 assert!(locator.pack(sfield::MemoData));
508
509 assert_eq!(locator.len(), 12); }
511
512 #[test]
513 fn test_pack_with_i32_still_works() {
514 let mut locator = Locator::new();
516
517 assert!(locator.pack(123i32));
518 assert!(locator.pack(456i32));
519
520 assert_eq!(locator.len(), 8); }
522
523 #[test]
524 fn test_repack_last_with_sfield() {
525 let mut locator = Locator::new();
526
527 locator.pack(sfield::Memos);
528 locator.pack(0);
529
530 assert!(locator.repack_last(sfield::MemoData));
532
533 assert_eq!(locator.len(), 8); }
535
536 #[test]
537 fn test_new_starts_empty() {
538 let locator = Locator::new();
539 assert_eq!(locator.len(), 0);
540 assert!(locator.is_empty());
541 }
542
543 #[test]
544 fn test_default_same_as_new() {
545 assert_eq!(Locator::default(), Locator::new());
546 }
547
548 #[test]
549 fn test_pack_writes_correct_bytes() {
550 let mut locator = Locator::new();
551 assert!(locator.pack(0x12345678i32));
552 assert_eq!(locator.len(), 4);
553
554 let bytes = unsafe { core::slice::from_raw_parts(locator.as_ptr(), 4) };
555 assert_eq!(bytes, &0x12345678i32.to_le_bytes());
556 }
557
558 #[test]
559 fn test_pack_returns_false_when_buffer_full() {
560 let mut locator = Locator::new();
561
562 for i in 0..16 {
564 assert!(locator.pack(i));
565 }
566 assert_eq!(locator.len(), 64);
567
568 assert!(!locator.pack(999i32));
570 assert_eq!(locator.len(), 64);
571 }
572
573 #[test]
574 fn test_is_empty_false_after_pack() {
575 let mut locator = Locator::new();
576 assert!(locator.is_empty());
577
578 locator.pack(sfield::Memos);
579 assert!(!locator.is_empty());
580 assert_eq!(locator.len(), 4);
581 }
582
583 #[test]
584 fn test_num_packed_bytes_equals_len() {
585 let mut locator = Locator::new();
586 assert_eq!(locator.num_packed_bytes(), locator.len());
587
588 locator.pack(sfield::Memos);
589 assert_eq!(locator.num_packed_bytes(), locator.len());
590 assert_eq!(locator.num_packed_bytes(), 4);
591
592 locator.pack(0);
593 assert_eq!(locator.num_packed_bytes(), locator.len());
594 assert_eq!(locator.num_packed_bytes(), 8);
595 }
596
597 #[test]
598 fn test_repack_last_on_empty_returns_false() {
599 let mut locator = Locator::new();
600 assert!(!locator.repack_last(sfield::Memos));
601 assert_eq!(locator.len(), 0);
602 }
603
604 #[test]
605 fn test_repack_last_overwrites_correct_bytes() {
606 let mut locator = Locator::new();
607 locator.pack(0x11111111i32);
608 locator.pack(0x22222222i32);
609 assert_eq!(locator.len(), 8);
610
611 assert!(locator.repack_last(0x33333333i32));
612 assert_eq!(locator.len(), 8);
613
614 let bytes = unsafe { core::slice::from_raw_parts(locator.as_ptr(), 8) };
615 assert_eq!(&bytes[0..4], &0x11111111i32.to_le_bytes());
617 assert_eq!(&bytes[4..8], &0x33333333i32.to_le_bytes());
619 }
620
621 use crate::host::error_codes::{FIELD_NOT_FOUND, SOME_ERROR};
624 use crate::host::host_bindings_trait::MockHostBindings;
625 use crate::host::setup_mock;
626 use crate::types::blob::StandardBlob;
627 use mockall::predicate::{always, eq};
628
629 fn packed(builder: &TxPathBuilder) -> &[u8] {
631 &builder.locator.buffer[..builder.locator.cur_buffer_index]
632 }
633
634 #[test]
635 fn test_tx_field_encodes_single_field_code() {
636 let builder = TxPathBuilder::for_current_tx().field(sfield::Sequence);
637
638 assert!(!builder.overflowed);
639 assert_eq!(packed(&builder), &i32::from(sfield::Sequence).to_le_bytes());
640 }
641
642 #[test]
643 fn test_tx_multi_hop_encodes_each_field_in_order() {
644 let builder = TxPathBuilder::for_current_tx()
645 .field(sfield::Memos)
646 .field(sfield::MemoData);
647
648 assert!(!builder.overflowed);
649 let bytes = packed(&builder);
650 assert_eq!(bytes.len(), 8);
651 assert_eq!(&bytes[0..4], &i32::from(sfield::Memos).to_le_bytes());
652 assert_eq!(&bytes[4..8], &i32::from(sfield::MemoData).to_le_bytes());
653 }
654
655 #[test]
656 fn test_tx_index_encodes_array_slot() {
657 let builder = TxPathBuilder::for_current_tx()
659 .field(sfield::Memos)
660 .index(2)
661 .field(sfield::MemoType);
662
663 assert!(!builder.overflowed);
664 let bytes = packed(&builder);
665 assert_eq!(bytes.len(), 12);
666 assert_eq!(&bytes[0..4], &i32::from(sfield::Memos).to_le_bytes());
667 assert_eq!(&bytes[4..8], &2u32.to_le_bytes());
668 assert_eq!(&bytes[8..12], &i32::from(sfield::MemoType).to_le_bytes());
669 }
670
671 #[test]
672 fn test_tx_overflow_via_field_sets_flag_and_stops_at_64_bytes() {
673 let mut builder = TxPathBuilder::for_current_tx();
675 for i in 0..16 {
676 builder = builder.index(i);
677 }
678 assert!(!builder.overflowed);
679 assert_eq!(builder.locator.num_packed_bytes(), 64);
680
681 let builder = builder.field(sfield::Sequence);
682 assert!(builder.overflowed);
683 assert_eq!(builder.locator.num_packed_bytes(), 64);
685 }
686
687 #[test]
688 fn test_tx_overflow_via_index_sets_flag_and_stops_at_64_bytes() {
689 let mut builder = TxPathBuilder::for_current_tx();
691 for _ in 0..16 {
692 builder = builder.field(sfield::Sequence);
693 }
694 assert!(!builder.overflowed);
695 assert_eq!(builder.locator.num_packed_bytes(), 64);
696
697 let builder = builder.index(99);
698 assert!(builder.overflowed);
699 assert_eq!(builder.locator.num_packed_bytes(), 64);
700 }
701
702 #[test]
703 fn test_tx_index_above_i32_max_is_malformed_not_a_negative_segment() {
704 let mut mock = MockHostBindings::new();
706 mock.expect_tx_inner().times(0);
707 let _guard = setup_mock(mock);
708
709 let builder = TxPathBuilder::for_current_tx()
710 .field(sfield::Memos)
711 .index(i32::MAX as u32 + 1);
712
713 assert!(builder.overflowed);
714 assert_eq!(builder.locator.num_packed_bytes(), 4);
716 assert_eq!(
717 builder.get::<u32>().err().unwrap().code(),
718 host::Error::LocatorMalformed.code()
719 );
720 }
721
722 #[test]
723 fn test_get_reads_and_decodes_inner_field() {
724 let mut mock = MockHostBindings::new();
725 mock.expect_tx_inner()
727 .with(always(), eq(12usize), always(), eq(4usize))
728 .times(1)
729 .returning(|_, _, _, _| 4);
730 let _guard = setup_mock(mock);
731
732 let result = TxPathBuilder::for_current_tx()
733 .field(sfield::Memos)
734 .index(0)
735 .field(sfield::MemoData)
736 .get::<u32>();
737
738 assert!(result.is_ok());
739 }
740
741 #[test]
742 fn test_get_returns_locator_malformed_when_overflowed_without_calling_host() {
743 let mut mock = MockHostBindings::new();
745 mock.expect_tx_inner().times(0);
746 let _guard = setup_mock(mock);
747
748 let mut builder = TxPathBuilder::for_current_tx();
749 for i in 0..17 {
750 builder = builder.index(i);
751 }
752 assert!(builder.overflowed);
753
754 let result = builder.get::<u32>();
755 assert!(result.is_err());
756 assert_eq!(
757 result.err().unwrap().code(),
758 host::Error::LocatorMalformed.code()
759 );
760 }
761
762 #[test]
763 fn test_get_propagates_host_error() {
764 let mut mock = MockHostBindings::new();
765 mock.expect_tx_inner()
766 .with(always(), eq(4usize), always(), eq(4usize))
767 .times(1)
768 .returning(|_, _, _, _| SOME_ERROR);
769 let _guard = setup_mock(mock);
770
771 let result = TxPathBuilder::for_current_tx()
772 .field(sfield::Sequence)
773 .get::<u32>();
774
775 assert!(result.is_err());
776 assert_eq!(result.err().unwrap().code(), SOME_ERROR);
777 }
778
779 #[test]
780 fn test_get_optional_returns_some_when_present() {
781 let mut mock = MockHostBindings::new();
782 mock.expect_tx_inner()
783 .with(always(), eq(12usize), always(), eq(4usize))
784 .times(1)
785 .returning(|_, _, _, _| 4);
786 let _guard = setup_mock(mock);
787
788 let result = TxPathBuilder::for_current_tx()
789 .field(sfield::Memos)
790 .index(0)
791 .field(sfield::MemoData)
792 .get_optional::<u32>();
793
794 assert!(result.is_ok());
795 assert!(result.unwrap().is_some());
796 }
797
798 #[test]
799 fn test_get_optional_returns_none_on_field_not_found() {
800 let mut mock = MockHostBindings::new();
801 mock.expect_tx_inner()
802 .with(always(), eq(4usize), always(), eq(4usize))
803 .times(1)
804 .returning(|_, _, _, _| FIELD_NOT_FOUND);
805 let _guard = setup_mock(mock);
806
807 let result = TxPathBuilder::for_current_tx()
808 .field(sfield::Sequence)
809 .get_optional::<u32>();
810
811 assert!(result.is_ok());
812 assert!(result.unwrap().is_none());
813 }
814
815 #[test]
816 fn test_tx_array_len_returns_count() {
817 let mut mock = MockHostBindings::new();
818 mock.expect_tx_inner_arr_len()
820 .with(always(), eq(4usize))
821 .times(1)
822 .returning(|_, _| 3);
823 let _guard = setup_mock(mock);
824
825 let result = TxPathBuilder::for_current_tx()
826 .field(sfield::Memos)
827 .array_len();
828
829 assert_eq!(result.unwrap(), 3);
830 }
831
832 #[test]
833 fn test_tx_array_len_zero_is_ok_not_an_error() {
834 let mut mock = MockHostBindings::new();
835 mock.expect_tx_inner_arr_len().times(1).returning(|_, _| 0);
836 let _guard = setup_mock(mock);
837
838 let result = TxPathBuilder::for_current_tx()
839 .field(sfield::Memos)
840 .array_len();
841
842 assert_eq!(result.unwrap(), 0);
843 }
844
845 #[test]
846 fn test_tx_array_len_propagates_host_error() {
847 use crate::host::error_codes::NO_ARRAY;
848 let mut mock = MockHostBindings::new();
849 mock.expect_tx_inner_arr_len()
850 .times(1)
851 .returning(|_, _| NO_ARRAY);
852 let _guard = setup_mock(mock);
853
854 let result = TxPathBuilder::for_current_tx()
855 .field(sfield::Sequence)
856 .array_len();
857
858 assert_eq!(result.err().unwrap().code(), NO_ARRAY);
859 }
860
861 #[test]
862 fn test_tx_array_len_returns_locator_malformed_when_overflowed_without_calling_host() {
863 let mut mock = MockHostBindings::new();
864 mock.expect_tx_inner_arr_len().times(0);
865 let _guard = setup_mock(mock);
866
867 let mut builder = TxPathBuilder::for_current_tx();
868 for i in 0..17 {
869 builder = builder.index(i);
870 }
871
872 assert_eq!(
873 builder.array_len().err().unwrap().code(),
874 host::Error::LocatorMalformed.code()
875 );
876 }
877
878 #[test]
879 fn test_tx_array_len_then_index_walks_every_entry() {
880 let mut mock = MockHostBindings::new();
882 mock.expect_tx_inner_arr_len()
883 .with(always(), eq(4usize))
884 .times(1)
885 .returning(|_, _| 2);
886 mock.expect_tx_inner()
888 .with(always(), eq(16usize), always(), always())
889 .times(2)
890 .returning(|_, _, _, out_buff_len| out_buff_len as i32);
891 let _guard = setup_mock(mock);
892
893 let tx = TxPathBuilder::for_current_tx();
894 let count = tx.clone().field(sfield::Memos).array_len().unwrap();
895 assert_eq!(count, 2);
896 for i in 0..count {
897 let memo_type = tx
898 .clone()
899 .field(sfield::Memos)
900 .index(i)
901 .field(sfield::Memo)
902 .field(sfield::MemoType)
903 .get::<StandardBlob>();
904 assert!(memo_type.is_ok());
905 }
906 }
907
908 fn ledger_packed(builder: &LedgerPathBuilder) -> &[u8] {
912 &builder.locator.buffer[..builder.locator.cur_buffer_index]
913 }
914
915 #[test]
916 fn test_ledger_field_encodes_single_field_code() {
917 let builder = LedgerPathBuilder::for_current_ledger_obj().field(sfield::Flags);
918
919 assert!(!builder.overflowed);
920 assert_eq!(
921 ledger_packed(&builder),
922 &i32::from(sfield::Flags).to_le_bytes()
923 );
924 }
925
926 #[test]
927 fn test_ledger_index_encodes_array_slot() {
928 let builder = LedgerPathBuilder::for_ledger_obj(1)
930 .field(sfield::SignerEntries)
931 .index(2)
932 .field(sfield::Account);
933
934 assert!(!builder.overflowed);
935 let bytes = ledger_packed(&builder);
936 assert_eq!(bytes.len(), 12);
937 assert_eq!(
938 &bytes[0..4],
939 &i32::from(sfield::SignerEntries).to_le_bytes()
940 );
941 assert_eq!(&bytes[4..8], &2u32.to_le_bytes());
942 assert_eq!(&bytes[8..12], &i32::from(sfield::Account).to_le_bytes());
943 }
944
945 #[test]
946 fn test_ledger_overflow_sets_flag_and_stops_at_64_bytes() {
947 let mut builder = LedgerPathBuilder::for_current_ledger_obj();
949 for i in 0..16 {
950 builder = builder.index(i);
951 }
952 assert!(!builder.overflowed);
953 assert_eq!(builder.locator.num_packed_bytes(), 64);
954
955 let builder = builder.field(sfield::Flags);
956 assert!(builder.overflowed);
957 assert_eq!(builder.locator.num_packed_bytes(), 64);
959 }
960
961 #[test]
962 fn test_ledger_index_above_i32_max_is_malformed_not_a_negative_segment() {
963 let mut mock = MockHostBindings::new();
965 mock.expect_le_inner().times(0);
966 let _guard = setup_mock(mock);
967
968 let builder = LedgerPathBuilder::for_ledger_obj(1)
969 .field(sfield::SignerEntries)
970 .index(i32::MAX as u32 + 1);
971
972 assert!(builder.overflowed);
973 assert_eq!(builder.locator.num_packed_bytes(), 4);
974 assert_eq!(
975 builder.get::<u32>().err().unwrap().code(),
976 host::Error::LocatorMalformed.code()
977 );
978 }
979
980 #[test]
981 fn test_ledger_current_get_reads_via_current_obj_host_fn() {
982 let mut mock = MockHostBindings::new();
984 mock.expect_home_le_inner()
985 .with(always(), eq(4usize), always(), eq(4usize))
986 .times(1)
987 .returning(|_, _, _, _| 4);
988 mock.expect_le_inner().times(0);
989 let _guard = setup_mock(mock);
990
991 let result = LedgerPathBuilder::for_current_ledger_obj()
992 .field(sfield::Flags)
993 .get::<u32>();
994
995 assert!(result.is_ok());
996 }
997
998 #[test]
999 fn test_ledger_by_slot_get_passes_slot_to_slot_host_fn() {
1000 const SLOT: i32 = 7;
1001 let mut mock = MockHostBindings::new();
1002 mock.expect_le_inner()
1004 .with(eq(SLOT), always(), eq(8usize), always(), eq(4usize))
1005 .times(1)
1006 .returning(|_, _, _, _, _| 4);
1007 mock.expect_home_le_inner().times(0);
1008 let _guard = setup_mock(mock);
1009
1010 let result = LedgerPathBuilder::for_ledger_obj(SLOT)
1011 .field(sfield::SignerEntries)
1012 .index(0)
1013 .get::<u32>();
1014
1015 assert!(result.is_ok());
1016 }
1017
1018 #[test]
1019 fn test_ledger_get_returns_locator_malformed_when_overflowed_without_calling_host() {
1020 let mut mock = MockHostBindings::new();
1022 mock.expect_le_inner().times(0);
1023 let _guard = setup_mock(mock);
1024
1025 let mut builder = LedgerPathBuilder::for_ledger_obj(1);
1026 for i in 0..17 {
1027 builder = builder.index(i);
1028 }
1029 assert!(builder.overflowed);
1030
1031 let result = builder.get::<u32>();
1032 assert!(result.is_err());
1033 assert_eq!(
1034 result.err().unwrap().code(),
1035 host::Error::LocatorMalformed.code()
1036 );
1037 }
1038
1039 #[test]
1040 fn test_ledger_get_propagates_host_error() {
1041 let mut mock = MockHostBindings::new();
1042 mock.expect_home_le_inner()
1043 .with(always(), eq(4usize), always(), eq(4usize))
1044 .times(1)
1045 .returning(|_, _, _, _| SOME_ERROR);
1046 let _guard = setup_mock(mock);
1047
1048 let result = LedgerPathBuilder::for_current_ledger_obj()
1049 .field(sfield::Flags)
1050 .get::<u32>();
1051
1052 assert!(result.is_err());
1053 assert_eq!(result.err().unwrap().code(), SOME_ERROR);
1054 }
1055
1056 #[test]
1057 fn test_ledger_get_optional_returns_some_when_present() {
1058 const SLOT: i32 = 2;
1059 let mut mock = MockHostBindings::new();
1060 mock.expect_le_inner()
1061 .with(eq(SLOT), always(), eq(4usize), always(), eq(4usize))
1062 .times(1)
1063 .returning(|_, _, _, _, _| 4);
1064 let _guard = setup_mock(mock);
1065
1066 let result = LedgerPathBuilder::for_ledger_obj(SLOT)
1067 .field(sfield::Flags)
1068 .get_optional::<u32>();
1069
1070 assert!(result.is_ok());
1071 assert!(result.unwrap().is_some());
1072 }
1073
1074 #[test]
1075 fn test_ledger_get_optional_returns_none_on_field_not_found() {
1076 let mut mock = MockHostBindings::new();
1077 mock.expect_home_le_inner()
1078 .with(always(), eq(4usize), always(), eq(4usize))
1079 .times(1)
1080 .returning(|_, _, _, _| FIELD_NOT_FOUND);
1081 let _guard = setup_mock(mock);
1082
1083 let result = LedgerPathBuilder::for_current_ledger_obj()
1084 .field(sfield::Flags)
1085 .get_optional::<u32>();
1086
1087 assert!(result.is_ok());
1088 assert!(result.unwrap().is_none());
1089 }
1090
1091 #[test]
1092 fn test_ledger_index_past_i32_max_marks_path_malformed_without_calling_host() {
1093 let mut mock = MockHostBindings::new();
1096 mock.expect_le_inner().times(0);
1097 mock.expect_le_inner_arr_len().times(0);
1098 let _guard = setup_mock(mock);
1099
1100 let builder = LedgerPathBuilder::for_ledger_obj(1)
1101 .field(sfield::SignerEntries)
1102 .index(u32::MAX);
1103
1104 assert!(builder.overflowed);
1105 assert_eq!(builder.locator.num_packed_bytes(), 4);
1107 assert_eq!(
1108 builder.get::<u32>().err().unwrap().code(),
1109 host::Error::LocatorMalformed.code()
1110 );
1111 assert_eq!(
1112 builder.array_len().err().unwrap().code(),
1113 host::Error::LocatorMalformed.code()
1114 );
1115 }
1116
1117 #[test]
1120 fn test_array_len_current_obj_returns_count() {
1121 let mut mock = MockHostBindings::new();
1122 mock.expect_home_le_inner_arr_len()
1123 .with(always(), eq(4usize))
1124 .times(1)
1125 .returning(|_, _| 3);
1126 let _guard = setup_mock(mock);
1127
1128 let result = LedgerPathBuilder::for_current_ledger_obj()
1129 .field(sfield::SignerEntries)
1130 .array_len();
1131
1132 assert_eq!(result.unwrap(), 3);
1133 }
1134
1135 #[test]
1136 fn test_array_len_by_slot_passes_slot_and_returns_count() {
1137 const SLOT: i32 = 4;
1138 let mut mock = MockHostBindings::new();
1139 mock.expect_le_inner_arr_len()
1140 .with(eq(SLOT), always(), eq(4usize))
1141 .times(1)
1142 .returning(|_, _, _| 2);
1143 mock.expect_home_le_inner_arr_len().times(0);
1144 let _guard = setup_mock(mock);
1145
1146 let result = LedgerPathBuilder::for_ledger_obj(SLOT)
1147 .field(sfield::PriceDataSeries)
1148 .array_len();
1149
1150 assert_eq!(result.unwrap(), 2);
1151 }
1152
1153 #[test]
1154 fn test_array_len_zero_is_ok_not_an_error() {
1155 let mut mock = MockHostBindings::new();
1157 mock.expect_home_le_inner_arr_len()
1158 .times(1)
1159 .returning(|_, _| 0);
1160 let _guard = setup_mock(mock);
1161
1162 let result = LedgerPathBuilder::for_current_ledger_obj()
1163 .field(sfield::SignerEntries)
1164 .array_len();
1165
1166 assert_eq!(result.unwrap(), 0);
1167 }
1168
1169 #[test]
1170 fn test_array_len_propagates_host_error() {
1171 let mut mock = MockHostBindings::new();
1172 mock.expect_home_le_inner_arr_len()
1173 .times(1)
1174 .returning(|_, _| SOME_ERROR);
1175 let _guard = setup_mock(mock);
1176
1177 let result = LedgerPathBuilder::for_current_ledger_obj()
1178 .field(sfield::SignerEntries)
1179 .array_len();
1180
1181 assert_eq!(result.err().unwrap().code(), SOME_ERROR);
1182 }
1183
1184 #[test]
1185 fn test_array_len_returns_locator_malformed_when_overflowed_without_calling_host() {
1186 let mut mock = MockHostBindings::new();
1187 mock.expect_le_inner_arr_len().times(0);
1188 let _guard = setup_mock(mock);
1189
1190 let mut builder = LedgerPathBuilder::for_ledger_obj(1);
1191 for i in 0..17 {
1192 builder = builder.index(i);
1193 }
1194
1195 assert_eq!(
1196 builder.array_len().err().unwrap().code(),
1197 host::Error::LocatorMalformed.code()
1198 );
1199 }
1200
1201 #[test]
1202 fn test_array_len_then_index_walks_every_entry() {
1203 const SLOT: i32 = 6;
1205 let mut mock = MockHostBindings::new();
1206 mock.expect_le_inner_arr_len()
1207 .with(eq(SLOT), always(), eq(4usize))
1208 .times(1)
1209 .returning(|_, _, _| 2);
1210 mock.expect_le_inner()
1212 .with(eq(SLOT), always(), eq(12usize), always(), eq(8usize))
1213 .times(2)
1214 .returning(|_, _, _, _, _| 8);
1215 let _guard = setup_mock(mock);
1216
1217 let obj = LedgerPathBuilder::for_ledger_obj(SLOT);
1218 let count = obj
1219 .clone()
1220 .field(sfield::PriceDataSeries)
1221 .array_len()
1222 .unwrap();
1223 assert_eq!(count, 2);
1224 for i in 0..count {
1225 let price = obj
1226 .clone()
1227 .field(sfield::PriceDataSeries)
1228 .index(i)
1229 .field(sfield::AssetPrice)
1230 .get::<u64>();
1231 assert!(price.is_ok());
1232 }
1233 }
1234}