xrpl_common_stdlib/fields/
current_ledger_obj.rs1use crate::fields::decoder::{FromLedger, decode_host_result};
15use crate::host::{Error, Result, home_le_field};
16use crate::sfield::SField;
17use crate::types::blob::Blob;
18use core::mem::MaybeUninit;
19
20#[inline]
28pub fn get_field<T: FromLedger, const CODE: i32>(_: SField<T, CODE>) -> Result<T> {
29 let mut buf = T::empty_buffer();
30 let n = {
31 let slice = buf.as_mut();
32 unsafe { home_le_field(CODE, slice.as_mut_ptr(), slice.len()) }
33 };
34 decode_host_result::<T>(buf, n)
35}
36
37#[inline]
46pub fn get_field_optional<T: FromLedger, const CODE: i32>(
47 field: SField<T, CODE>,
48) -> Result<Option<T>> {
49 match get_field(field) {
50 Result::Ok(value) => Result::Ok(Some(value)),
51 Result::Err(Error::FieldNotFound) => Result::Ok(None),
52 Result::Err(e) => Result::Err(e),
53 }
54}
55
56#[inline]
72pub fn get_blob_field<const N: usize, const CODE: i32>(
73 _: SField<Blob<N>, CODE>,
74) -> Result<Blob<N>> {
75 let mut blob = MaybeUninit::<Blob<N>>::uninit();
76 let data_ptr = unsafe { core::ptr::addr_of_mut!((*blob.as_mut_ptr()).data) } as *mut u8;
79 unsafe { core::ptr::write_bytes(data_ptr, 0u8, N) };
84 let n = unsafe { home_le_field(CODE, data_ptr, N) };
85 if n < 0 {
86 return Result::Err(Error::from_code(n));
87 }
88 if n as usize > N {
89 return Result::Err(Error::PointerOutOfBounds);
91 }
92 unsafe {
95 core::ptr::addr_of_mut!((*blob.as_mut_ptr()).len).write(n as usize);
96 Result::Ok(blob.assume_init())
97 }
98}
99
100#[inline]
106pub fn get_blob_field_optional<const N: usize, const CODE: i32>(
107 field: SField<Blob<N>, CODE>,
108) -> Result<Option<Blob<N>>> {
109 match get_blob_field(field) {
110 Result::Ok(blob) => Result::Ok(Some(blob)),
111 Result::Err(Error::FieldNotFound) => Result::Ok(None),
112 Result::Err(e) => Result::Err(e),
113 }
114}
115
116#[cfg(test)]
117mod tests {
118 use super::{get_blob_field, get_blob_field_optional, get_field, get_field_optional};
119 use crate::host::error_codes::{FIELD_NOT_FOUND, SOME_ERROR};
120 use crate::host::host_bindings_trait::MockHostBindings;
121 use crate::host::setup_mock;
122 use crate::sfield;
123 use crate::types::account_id::{ACCOUNT_ID_SIZE, AccountID};
124 use mockall::predicate::{always, eq};
125
126 fn expect_current_field(
127 mock: &mut MockHostBindings,
128 field_code: i32,
129 size: usize,
130 times: usize,
131 ) {
132 mock.expect_home_le_field()
133 .with(eq(field_code), always(), eq(size))
134 .times(times)
135 .returning(move |_, _, _| size as i32);
136 }
137
138 #[test]
139 fn test_get_field_success() {
140 let mut mock = MockHostBindings::new();
141 expect_current_field(&mut mock, sfield::Sequence.into(), 4, 1);
142 expect_current_field(&mut mock, sfield::Account.into(), ACCOUNT_ID_SIZE, 1);
143 let _guard = setup_mock(mock);
144
145 assert!(get_field::<u32, _>(sfield::Sequence).is_ok());
146 assert!(get_field::<AccountID, _>(sfield::Account).is_ok());
147 }
148
149 #[test]
150 fn test_get_field_optional_returns_none_on_field_not_found() {
151 let mut mock = MockHostBindings::new();
152 mock.expect_home_le_field()
153 .with(eq::<i32>(sfield::SourceTag.into()), always(), eq(4))
154 .times(1)
155 .returning(|_, _, _| FIELD_NOT_FOUND);
156 let _guard = setup_mock(mock);
157
158 let result = get_field_optional::<u32, _>(sfield::SourceTag);
159 assert!(result.is_ok());
160 assert!(result.unwrap().is_none());
161 }
162
163 #[test]
164 fn test_get_field_optional_returns_some_when_present() {
165 let mut mock = MockHostBindings::new();
166 expect_current_field(&mut mock, sfield::SourceTag.into(), 4, 1);
167 let _guard = setup_mock(mock);
168
169 let result = get_field_optional::<u32, _>(sfield::SourceTag);
170 assert!(result.is_ok());
171 assert!(result.unwrap().is_some());
172 }
173
174 #[test]
175 fn test_get_field_returns_decode_error_on_byte_mismatch() {
176 let mut mock = MockHostBindings::new();
179 mock.expect_home_le_field()
180 .with(eq::<i32>(sfield::Sequence.into()), always(), eq(4))
181 .times(1)
182 .returning(|_, _, _| 3);
183 let _guard = setup_mock(mock);
184
185 let result = get_field::<u32, _>(sfield::Sequence);
186 assert!(result.is_err());
187 assert_eq!(
188 result.err().unwrap().code(),
189 crate::host::Error::InvalidDecoding.code()
190 );
191 }
192
193 #[test]
194 fn test_get_field_returns_err_on_internal_error() {
195 let mut mock = MockHostBindings::new();
196 mock.expect_home_le_field()
197 .with(eq::<i32>(sfield::Flags.into()), always(), eq(4))
198 .times(1)
199 .returning(|_, _, _| SOME_ERROR);
200 let _guard = setup_mock(mock);
201
202 let result = get_field::<u32, _>(sfield::Flags);
203 assert!(result.is_err());
204 assert_eq!(result.err().unwrap().code(), SOME_ERROR);
205 }
206
207 #[test]
208 fn test_get_field_returns_err_when_host_reports_oversized_write() {
209 let mut mock = MockHostBindings::new();
212 mock.expect_home_le_field()
213 .with(eq::<i32>(sfield::Sequence.into()), always(), eq(4))
214 .times(1)
215 .returning(|_, _, _| 8); let _guard = setup_mock(mock);
217
218 let result = get_field::<u32, _>(sfield::Sequence);
219 assert!(result.is_err());
220 assert_eq!(
221 result.err().unwrap().code(),
222 crate::host::Error::PointerOutOfBounds.code()
223 );
224 }
225
226 #[test]
227 fn test_get_blob_field_writes_bytes_directly_into_blob_data() {
228 let mut mock = MockHostBindings::new();
229 mock.expect_home_le_field()
230 .with(eq::<i32>(sfield::Condition.into()), always(), eq(128))
231 .times(1)
232 .returning(|_, buf, size| {
233 let slice = unsafe { core::slice::from_raw_parts_mut(buf, size) };
235 slice.fill(0xAB);
236 size as i32
237 });
238 let _guard = setup_mock(mock);
239
240 let blob = get_blob_field(sfield::Condition).unwrap();
241 assert_eq!(blob.len(), 128);
242 assert!(blob.as_slice().iter().all(|&b| b == 0xAB));
243 }
244
245 #[test]
246 fn test_get_blob_field_zeroes_tail_when_host_writes_fewer_bytes() {
247 let mut mock = MockHostBindings::new();
250 mock.expect_home_le_field()
251 .with(eq::<i32>(sfield::Condition.into()), always(), eq(128))
252 .times(1)
253 .returning(|_, buf, _size| {
254 let slice = unsafe { core::slice::from_raw_parts_mut(buf, 10) };
255 slice.fill(0xFF);
256 10
257 });
258 let _guard = setup_mock(mock);
259
260 let blob = get_blob_field(sfield::Condition).unwrap();
261 assert_eq!(blob.len(), 10);
262 assert_eq!(blob.data[9], 0xFF);
263 assert_eq!(blob.data[10], 0);
264 assert_eq!(blob.data[127], 0);
265 }
266
267 #[test]
268 fn test_get_blob_field_returns_err_on_internal_error() {
269 let mut mock = MockHostBindings::new();
270 mock.expect_home_le_field()
271 .with(eq::<i32>(sfield::Condition.into()), always(), eq(128))
272 .times(1)
273 .returning(|_, _, _| SOME_ERROR);
274 let _guard = setup_mock(mock);
275
276 let result = get_blob_field(sfield::Condition);
277 assert!(result.is_err());
278 assert_eq!(result.err().unwrap().code(), SOME_ERROR);
279 }
280
281 #[test]
282 fn test_get_blob_field_returns_err_when_host_reports_oversized_write() {
283 let mut mock = MockHostBindings::new();
284 mock.expect_home_le_field()
285 .with(eq::<i32>(sfield::Condition.into()), always(), eq(128))
286 .times(1)
287 .returning(|_, _, _| 129); let _guard = setup_mock(mock);
289
290 let result = get_blob_field(sfield::Condition);
291 assert!(result.is_err());
292 assert_eq!(
293 result.err().unwrap().code(),
294 crate::host::Error::PointerOutOfBounds.code()
295 );
296 }
297
298 #[test]
299 fn test_get_blob_field_optional_returns_none_on_field_not_found() {
300 let mut mock = MockHostBindings::new();
301 mock.expect_home_le_field()
302 .with(eq::<i32>(sfield::Condition.into()), always(), eq(128))
303 .times(1)
304 .returning(|_, _, _| FIELD_NOT_FOUND);
305 let _guard = setup_mock(mock);
306
307 let result = get_blob_field_optional(sfield::Condition);
308 assert!(result.is_ok());
309 assert!(result.unwrap().is_none());
310 }
311
312 #[test]
313 fn test_get_blob_field_optional_returns_some_when_present() {
314 let mut mock = MockHostBindings::new();
315 expect_current_field(&mut mock, sfield::Condition.into(), 128, 1);
316 let _guard = setup_mock(mock);
317
318 let result = get_blob_field_optional(sfield::Condition);
319 assert!(result.is_ok());
320 assert!(result.unwrap().is_some());
321 }
322}