xrpl_common_stdlib/fields/
current_tx.rs1use crate::fields::decoder::{FromCurrentTx, decode_host_result};
28use crate::host::{Error, Result, tx_field};
29use crate::sfield::SField;
30use crate::types::blob::Blob;
31use core::mem::MaybeUninit;
32
33#[inline]
56pub fn get_field<T: FromCurrentTx, const CODE: i32>(_: SField<T, CODE>) -> Result<T> {
57 let mut buf = T::empty_buffer();
58 let n = {
59 let slice = buf.as_mut();
60 unsafe { tx_field(CODE, slice.as_mut_ptr(), slice.len()) }
61 };
62 decode_host_result::<T>(buf, n)
63}
64
65#[inline]
89pub fn get_field_optional<T: FromCurrentTx, const CODE: i32>(
90 field: SField<T, CODE>,
91) -> Result<Option<T>> {
92 match get_field(field) {
93 Result::Ok(value) => Result::Ok(Some(value)),
94 Result::Err(Error::FieldNotFound) => Result::Ok(None),
95 Result::Err(e) => Result::Err(e),
96 }
97}
98
99#[inline]
115pub fn get_blob_field<const N: usize, const CODE: i32>(
116 _: SField<Blob<N>, CODE>,
117) -> Result<Blob<N>> {
118 let mut blob = MaybeUninit::<Blob<N>>::uninit();
119 let data_ptr = unsafe { core::ptr::addr_of_mut!((*blob.as_mut_ptr()).data) } as *mut u8;
122 unsafe { core::ptr::write_bytes(data_ptr, 0u8, N) };
127 let n = unsafe { tx_field(CODE, data_ptr, N) };
128 if n < 0 {
129 return Result::Err(Error::from_code(n));
130 }
131 if n as usize > N {
132 return Result::Err(Error::PointerOutOfBounds);
134 }
135 unsafe {
138 core::ptr::addr_of_mut!((*blob.as_mut_ptr()).len).write(n as usize);
139 Result::Ok(blob.assume_init())
140 }
141}
142
143#[inline]
149pub fn get_blob_field_optional<const N: usize, const CODE: i32>(
150 field: SField<Blob<N>, CODE>,
151) -> Result<Option<Blob<N>>> {
152 match get_blob_field(field) {
153 Result::Ok(blob) => Result::Ok(Some(blob)),
154 Result::Err(Error::FieldNotFound) => Result::Ok(None),
155 Result::Err(e) => Result::Err(e),
156 }
157}
158
159#[cfg(test)]
160mod tests {
161 use super::{get_blob_field, get_blob_field_optional, get_field, get_field_optional};
162 use crate::fields::decoder::FieldDecoder;
163 use crate::host::error_codes::{FIELD_NOT_FOUND, SOME_ERROR};
164 use crate::host::host_bindings_trait::MockHostBindings;
165 use crate::host::setup_mock;
166 use crate::sfield;
167 use crate::types::account_id::{ACCOUNT_ID_SIZE, AccountID};
168 use crate::types::number::Number;
169 use mockall::predicate::{always, eq};
170
171 fn expect_tx_field(mock: &mut MockHostBindings, field_code: i32, size: usize, times: usize) {
172 mock.expect_tx_field()
173 .with(eq(field_code), always(), eq(size))
174 .times(times)
175 .returning(move |_, _, _| size as i32);
176 }
177
178 #[test]
179 fn test_get_field_success() {
180 let mut mock = MockHostBindings::new();
181 expect_tx_field(&mut mock, sfield::Sequence.into(), 4, 1);
182 expect_tx_field(&mut mock, sfield::Account.into(), ACCOUNT_ID_SIZE, 1);
183 let _guard = setup_mock(mock);
184
185 assert!(get_field::<u32, _>(sfield::Sequence).is_ok());
186 assert!(get_field::<AccountID, _>(sfield::Account).is_ok());
187 }
188
189 #[test]
190 fn test_get_field_decodes_stnumber_field() {
191 let mut mock = MockHostBindings::new();
194 expect_tx_field(&mut mock, sfield::PeriodicPayment.into(), 12, 1);
195 let _guard = setup_mock(mock);
196
197 assert_eq!(
198 get_field(sfield::PeriodicPayment).unwrap(),
199 Number::from([0u8; 12])
200 );
201 }
202
203 #[test]
204 fn test_get_field_optional_returns_none_on_field_not_found() {
205 let mut mock = MockHostBindings::new();
206 mock.expect_tx_field()
207 .with(eq::<i32>(sfield::SourceTag.into()), always(), eq(4))
208 .times(1)
209 .returning(|_, _, _| FIELD_NOT_FOUND);
210 let _guard = setup_mock(mock);
211
212 let result = get_field_optional::<u32, _>(sfield::SourceTag);
213 assert!(result.is_ok());
214 assert!(result.unwrap().is_none());
215 }
216
217 #[test]
218 fn test_get_field_optional_returns_some_when_present() {
219 let mut mock = MockHostBindings::new();
220 expect_tx_field(&mut mock, sfield::SourceTag.into(), 4, 1);
221 let _guard = setup_mock(mock);
222
223 let result = get_field_optional::<u32, _>(sfield::SourceTag);
224 assert!(result.is_ok());
225 assert!(result.unwrap().is_some());
226 }
227
228 #[test]
229 fn test_get_field_returns_decode_error_on_byte_mismatch() {
230 let mut mock = MockHostBindings::new();
233 mock.expect_tx_field()
234 .with(eq::<i32>(sfield::Sequence.into()), always(), eq(4))
235 .times(1)
236 .returning(|_, _, _| 3);
237 let _guard = setup_mock(mock);
238
239 let result = get_field::<u32, _>(sfield::Sequence);
240 assert!(result.is_err());
241 assert_eq!(
242 result.err().unwrap().code(),
243 crate::host::Error::InvalidDecoding.code()
244 );
245 }
246
247 #[test]
248 fn test_get_field_returns_err_on_internal_error() {
249 let mut mock = MockHostBindings::new();
250 mock.expect_tx_field()
251 .with(eq::<i32>(sfield::Flags.into()), always(), eq(4))
252 .times(1)
253 .returning(|_, _, _| SOME_ERROR);
254 let _guard = setup_mock(mock);
255
256 let result = get_field::<u32, _>(sfield::Flags);
257 assert!(result.is_err());
258 assert_eq!(result.err().unwrap().code(), SOME_ERROR);
259 }
260
261 #[test]
262 fn test_u16_decodes_little_endian_host_bytes() {
263 let result = u16::decode([0x02, 0x01], 2);
264 assert_eq!(result.unwrap(), 0x0102u16);
265 }
266
267 #[test]
268 fn test_u32_decodes_little_endian_host_bytes() {
269 let result = u32::decode([0x04, 0x03, 0x02, 0x01], 4);
270 assert_eq!(result.unwrap(), 0x01020304u32);
271 }
272
273 #[test]
274 fn test_u64_decodes_little_endian_host_bytes() {
275 let result = u64::decode([0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01], 8);
276 assert_eq!(result.unwrap(), 0x0102030405060708u64);
277 }
278
279 #[test]
280 fn test_get_field_returns_err_when_host_reports_oversized_write() {
281 let mut mock = MockHostBindings::new();
284 mock.expect_tx_field()
285 .with(eq::<i32>(sfield::Sequence.into()), always(), eq(4))
286 .times(1)
287 .returning(|_, _, _| 8); let _guard = setup_mock(mock);
289
290 let result = get_field::<u32, _>(sfield::Sequence);
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_writes_bytes_directly_into_blob_data() {
300 let mut mock = MockHostBindings::new();
301 mock.expect_tx_field()
302 .with(eq::<i32>(sfield::PublicKey.into()), always(), eq(33))
303 .times(1)
304 .returning(|_, buf, size| {
305 let slice = unsafe { core::slice::from_raw_parts_mut(buf, size) };
307 slice.fill(0xAB);
308 size as i32
309 });
310 let _guard = setup_mock(mock);
311
312 let blob = get_blob_field(sfield::PublicKey).unwrap();
313 assert_eq!(blob.len(), 33);
314 assert!(blob.as_slice().iter().all(|&b| b == 0xAB));
315 }
316
317 #[test]
318 fn test_get_blob_field_zeroes_tail_when_host_writes_fewer_bytes() {
319 let mut mock = MockHostBindings::new();
322 mock.expect_tx_field()
323 .with(eq::<i32>(sfield::PublicKey.into()), always(), eq(33))
324 .times(1)
325 .returning(|_, buf, _size| {
326 let slice = unsafe { core::slice::from_raw_parts_mut(buf, 10) };
327 slice.fill(0xFF);
328 10
329 });
330 let _guard = setup_mock(mock);
331
332 let blob = get_blob_field(sfield::PublicKey).unwrap();
333 assert_eq!(blob.len(), 10);
334 assert_eq!(blob.data[9], 0xFF);
335 assert_eq!(blob.data[10], 0);
336 assert_eq!(blob.data[32], 0);
337 }
338
339 #[test]
340 fn test_get_blob_field_returns_err_on_internal_error() {
341 let mut mock = MockHostBindings::new();
342 mock.expect_tx_field()
343 .with(eq::<i32>(sfield::PublicKey.into()), always(), eq(33))
344 .times(1)
345 .returning(|_, _, _| SOME_ERROR);
346 let _guard = setup_mock(mock);
347
348 let result = get_blob_field(sfield::PublicKey);
349 assert!(result.is_err());
350 assert_eq!(result.err().unwrap().code(), SOME_ERROR);
351 }
352
353 #[test]
354 fn test_get_blob_field_returns_err_when_host_reports_oversized_write() {
355 let mut mock = MockHostBindings::new();
356 mock.expect_tx_field()
357 .with(eq::<i32>(sfield::PublicKey.into()), always(), eq(33))
358 .times(1)
359 .returning(|_, _, _| 34); let _guard = setup_mock(mock);
361
362 let result = get_blob_field(sfield::PublicKey);
363 assert!(result.is_err());
364 assert_eq!(
365 result.err().unwrap().code(),
366 crate::host::Error::PointerOutOfBounds.code()
367 );
368 }
369
370 #[test]
371 fn test_get_blob_field_optional_returns_none_on_field_not_found() {
372 let mut mock = MockHostBindings::new();
373 mock.expect_tx_field()
374 .with(eq::<i32>(sfield::PublicKey.into()), always(), eq(33))
375 .times(1)
376 .returning(|_, _, _| FIELD_NOT_FOUND);
377 let _guard = setup_mock(mock);
378
379 let result = get_blob_field_optional(sfield::PublicKey);
380 assert!(result.is_ok());
381 assert!(result.unwrap().is_none());
382 }
383
384 #[test]
385 fn test_get_blob_field_optional_returns_some_when_present() {
386 let mut mock = MockHostBindings::new();
387 mock.expect_tx_field()
388 .with(eq::<i32>(sfield::PublicKey.into()), always(), eq(33))
389 .times(1)
390 .returning(|_, _, _| 33);
391 let _guard = setup_mock(mock);
392
393 let result = get_blob_field_optional(sfield::PublicKey);
394 assert!(result.is_ok());
395 assert!(result.unwrap().is_some());
396 }
397}