xrpl_escrow_stdlib/ledger_objects/
traits.rs1use xrpl_common_stdlib::core::ledger_objects::current_ledger_object;
4use xrpl_common_stdlib::core::ledger_objects::traits::CurrentLedgerObjectCommonFields;
5use xrpl_common_stdlib::core::types::account_id::AccountID;
6use xrpl_common_stdlib::core::types::amount::Amount;
7use xrpl_common_stdlib::core::types::blob::{ConditionBlob, WasmBlob};
8use xrpl_common_stdlib::core::types::contract_data::{ContractData, XRPL_CONTRACT_DATA_SIZE};
9use xrpl_common_stdlib::core::types::uint::Hash256;
10use xrpl_common_stdlib::host::error_codes::{match_result_code, match_result_code_optional};
11use xrpl_common_stdlib::host::{Error, get_current_ledger_obj_field, update_data};
12use xrpl_common_stdlib::host::{Result, Result::Err, Result::Ok};
13use xrpl_common_stdlib::sfield;
14
15pub trait CurrentEscrowFields: CurrentLedgerObjectCommonFields {
20 fn get_account(&self) -> Result<AccountID> {
23 current_ledger_object::get_field(sfield::Account)
24 }
25
26 fn get_amount(&self) -> Result<Amount> {
28 current_ledger_object::get_field(sfield::Amount)
29 }
30
31 fn get_cancel_after(&self) -> Result<Option<u32>> {
35 current_ledger_object::get_field_optional(sfield::CancelAfter)
36 }
37
38 fn get_condition(&self) -> Result<Option<ConditionBlob>> {
41 let mut buffer = ConditionBlob::new();
42 let result_code = unsafe {
43 get_current_ledger_obj_field(
44 sfield::Condition.into(),
45 buffer.data.as_mut_ptr(),
46 buffer.capacity(),
47 )
48 };
49 match_result_code_optional(result_code, || {
50 buffer.len = result_code as usize;
51 (result_code > 0).then_some(buffer)
52 })
53 }
54
55 fn get_destination(&self) -> Result<AccountID> {
57 current_ledger_object::get_field(sfield::Destination)
58 }
59
60 fn get_destination_node(&self) -> Result<Option<u64>> {
63 current_ledger_object::get_field_optional(sfield::DestinationNode)
64 }
65
66 fn get_destination_tag(&self) -> Result<Option<u32>> {
69 current_ledger_object::get_field_optional(sfield::DestinationTag)
70 }
71
72 fn get_finish_after(&self) -> Result<Option<u32>> {
76 current_ledger_object::get_field_optional(sfield::FinishAfter)
77 }
78
79 fn get_owner_node(&self) -> Result<u64> {
82 current_ledger_object::get_field(sfield::OwnerNode)
83 }
84
85 fn get_previous_txn_id(&self) -> Result<Hash256> {
87 current_ledger_object::get_field(sfield::PreviousTxnID)
88 }
89
90 fn get_previous_txn_lgr_seq(&self) -> Result<u32> {
93 current_ledger_object::get_field(sfield::PreviousTxnLgrSeq)
94 }
95
96 fn get_source_tag(&self) -> Result<Option<u32>> {
99 current_ledger_object::get_field_optional(sfield::SourceTag)
100 }
101
102 fn get_finish_function(&self) -> Result<Option<WasmBlob>> {
104 current_ledger_object::get_field_optional(sfield::FinishFunction)
105 }
106
107 fn get_data(&self) -> Result<ContractData> {
118 let mut data: [u8; XRPL_CONTRACT_DATA_SIZE] = [0; XRPL_CONTRACT_DATA_SIZE];
119
120 let result_code = unsafe {
121 get_current_ledger_obj_field(sfield::Data.into(), data.as_mut_ptr(), data.len())
122 };
123
124 match result_code {
125 code if code >= 0 => Ok(ContractData {
126 data,
127 len: code as usize,
128 }),
129 code => Err(Error::from_code(code)),
130 }
131 }
132
133 fn update_current_escrow_data(data: ContractData) -> Result<()> {
145 let result_code = unsafe { update_data(data.data.as_ptr(), data.len) };
149 match_result_code(result_code, || ())
150 }
151}
152
153#[cfg(test)]
154mod tests {
155 use super::*;
156 use mockall::predicate::{always, eq};
157 use xrpl_common_stdlib::core::ledger_objects::LedgerObjectFieldGetter;
158 use xrpl_common_stdlib::host::error_codes::{FIELD_NOT_FOUND, INTERNAL_ERROR, INVALID_FIELD};
159 use xrpl_common_stdlib::host::host_bindings_trait::MockHostBindings;
160 use xrpl_common_stdlib::sfield::SField;
161
162 fn expect_current_field<
163 T: LedgerObjectFieldGetter + Send + std::fmt::Debug + PartialEq + 'static,
164 const CODE: i32,
165 >(
166 mock: &mut MockHostBindings,
167 _field: SField<T, CODE>,
168 size: usize,
169 times: usize,
170 ) {
171 mock.expect_get_current_ledger_obj_field()
172 .with(eq(CODE), always(), eq(size))
173 .times(times)
174 .returning(move |_, _, _| size as i32);
175 }
176
177 mod current_escrow_fields {
178 use super::*;
179 use crate::ledger_objects::current_escrow::CurrentEscrow;
180 use xrpl_common_stdlib::core::types::blob::CONDITION_BLOB_SIZE;
181 use xrpl_common_stdlib::core::types::blob::WASM_BLOB_SIZE;
182 use xrpl_common_stdlib::host::setup_mock;
183
184 #[test]
185 fn test_mandatory_fields_return_ok() {
186 let mut mock = MockHostBindings::new();
187
188 expect_current_field(&mut mock, sfield::Account, 20, 1);
190 expect_current_field(&mut mock, sfield::Amount, 48, 1);
192 expect_current_field(&mut mock, sfield::Destination, 20, 1);
194 expect_current_field(&mut mock, sfield::OwnerNode, 8, 1);
196 expect_current_field(&mut mock, sfield::PreviousTxnID, 32, 1);
198 expect_current_field(&mut mock, sfield::PreviousTxnLgrSeq, 4, 1);
200 expect_current_field(&mut mock, sfield::Data, 4096, 1);
202
203 let _guard = setup_mock(mock);
204
205 let escrow = CurrentEscrow;
206
207 assert!(escrow.get_account().is_ok());
209 assert!(escrow.get_amount().is_ok());
210 assert!(escrow.get_destination().is_ok());
211 assert!(escrow.get_owner_node().is_ok());
212 assert!(escrow.get_previous_txn_id().is_ok());
213 assert!(escrow.get_previous_txn_lgr_seq().is_ok());
214 assert!(escrow.get_data().is_ok());
215 }
216
217 #[test]
218 fn test_optional_fields_return_some() {
219 let mut mock = MockHostBindings::new();
220
221 expect_current_field(&mut mock, sfield::CancelAfter, 4, 1);
223 expect_current_field(&mut mock, sfield::Condition, CONDITION_BLOB_SIZE, 1);
225 expect_current_field(&mut mock, sfield::DestinationNode, 8, 1);
227 expect_current_field(&mut mock, sfield::DestinationTag, 4, 1);
229 expect_current_field(&mut mock, sfield::FinishAfter, 4, 1);
231 expect_current_field(&mut mock, sfield::SourceTag, 4, 1);
233 expect_current_field(&mut mock, sfield::FinishFunction, WASM_BLOB_SIZE, 1);
235
236 let _guard = setup_mock(mock);
237
238 let escrow = CurrentEscrow;
239
240 assert!(escrow.get_cancel_after().unwrap().is_some());
242 assert!(escrow.get_condition().unwrap().is_some());
243 assert!(escrow.get_destination_node().unwrap().is_some());
244 assert!(escrow.get_destination_tag().unwrap().is_some());
245 assert!(escrow.get_finish_after().unwrap().is_some());
246 assert!(escrow.get_source_tag().unwrap().is_some());
247 assert!(escrow.get_finish_function().unwrap().is_some());
248 }
249
250 #[test]
251 fn test_optional_fields_return_none_when_field_not_found() {
252 let mut mock = MockHostBindings::new();
253
254 mock.expect_get_current_ledger_obj_field()
256 .with(eq(sfield::CancelAfter), always(), eq(4))
257 .times(1)
258 .returning(|_, _, _| FIELD_NOT_FOUND);
259 mock.expect_get_current_ledger_obj_field()
261 .with(eq(sfield::Condition), always(), eq(CONDITION_BLOB_SIZE))
262 .times(1)
263 .returning(|_, _, _| 0);
264 mock.expect_get_current_ledger_obj_field()
266 .with(eq(sfield::DestinationNode), always(), eq(8))
267 .times(1)
268 .returning(|_, _, _| FIELD_NOT_FOUND);
269 mock.expect_get_current_ledger_obj_field()
271 .with(eq(sfield::DestinationTag), always(), eq(4))
272 .times(1)
273 .returning(|_, _, _| FIELD_NOT_FOUND);
274 mock.expect_get_current_ledger_obj_field()
276 .with(eq(sfield::FinishAfter), always(), eq(4))
277 .times(1)
278 .returning(|_, _, _| FIELD_NOT_FOUND);
279 mock.expect_get_current_ledger_obj_field()
281 .with(eq(sfield::SourceTag), always(), eq(4))
282 .times(1)
283 .returning(|_, _, _| FIELD_NOT_FOUND);
284 mock.expect_get_current_ledger_obj_field()
286 .with(eq(sfield::FinishFunction), always(), eq(WASM_BLOB_SIZE))
287 .times(1)
288 .returning(|_, _, _| 0);
289
290 let _guard = setup_mock(mock);
291
292 let escrow = CurrentEscrow;
293
294 assert!(escrow.get_cancel_after().unwrap().is_none());
296 assert!(escrow.get_condition().unwrap().is_none());
297 assert!(escrow.get_destination_node().unwrap().is_none());
298 assert!(escrow.get_destination_tag().unwrap().is_none());
299 assert!(escrow.get_finish_after().unwrap().is_none());
300 assert!(escrow.get_source_tag().unwrap().is_none());
301
302 let finish_function = escrow.get_finish_function().unwrap();
304 assert!(finish_function.is_some());
305 assert_eq!(finish_function.unwrap().len, 0);
306 }
307
308 #[test]
309 fn test_mandatory_fields_return_error_on_internal_error() {
310 let mut mock = MockHostBindings::new();
311
312 mock.expect_get_current_ledger_obj_field()
314 .with(eq(sfield::Account), always(), eq(20))
315 .times(1)
316 .returning(|_, _, _| INTERNAL_ERROR);
317
318 let _guard = setup_mock(mock);
319
320 let escrow = CurrentEscrow;
321 let result = escrow.get_account();
322
323 assert!(result.is_err());
324 assert_eq!(result.err().unwrap().code(), INTERNAL_ERROR);
325 }
326
327 #[test]
328 fn test_get_data_returns_error_on_internal_error() {
329 let mut mock = MockHostBindings::new();
330
331 mock.expect_get_current_ledger_obj_field()
332 .with(eq(sfield::Data), always(), eq(4096))
333 .times(1)
334 .returning(|_, _, _| INTERNAL_ERROR);
335
336 let _guard = setup_mock(mock);
337
338 let escrow = CurrentEscrow;
339 let result = escrow.get_data();
340
341 assert!(result.is_err());
342 assert_eq!(result.err().unwrap().code(), INTERNAL_ERROR);
343 }
344
345 #[test]
346 fn test_mandatory_fields_return_error_on_invalid_field() {
347 let mut mock = MockHostBindings::new();
348
349 mock.expect_get_current_ledger_obj_field()
351 .with(eq(sfield::Account), always(), eq(20))
352 .times(1)
353 .returning(|_, _, _| INVALID_FIELD);
354
355 let _guard = setup_mock(mock);
356
357 let escrow = CurrentEscrow;
358 let result = escrow.get_account();
359
360 assert!(result.is_err());
361 assert_eq!(result.err().unwrap().code(), INVALID_FIELD);
362 }
363 }
364}