xrpl_escrow_stdlib/ledger_objects/
traits.rs1use xrpl_common_stdlib::host::error_codes::match_result_code;
4use xrpl_common_stdlib::host::{Error, home_le_field, set_data};
5use xrpl_common_stdlib::host::{Result, Result::Err, Result::Ok};
6use xrpl_common_stdlib::objects::current_ledger_object;
7use xrpl_common_stdlib::objects::traits::CurrentLedgerObjectCommonFields;
8use xrpl_common_stdlib::sfield;
9use xrpl_common_stdlib::types::account_id::AccountID;
10use xrpl_common_stdlib::types::amount::Amount;
11use xrpl_common_stdlib::types::blob::{ConditionBlob, WasmBlob};
12use xrpl_common_stdlib::types::contract_data::{ContractData, XRPL_CONTRACT_DATA_SIZE};
13use xrpl_common_stdlib::types::uint::Hash256;
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 current_ledger_object::get_blob_field_optional(sfield::Condition)
42 }
43
44 fn get_destination(&self) -> Result<AccountID> {
46 current_ledger_object::get_field(sfield::Destination)
47 }
48
49 fn get_destination_node(&self) -> Result<Option<u64>> {
52 current_ledger_object::get_field_optional(sfield::DestinationNode)
53 }
54
55 fn get_destination_tag(&self) -> Result<Option<u32>> {
58 current_ledger_object::get_field_optional(sfield::DestinationTag)
59 }
60
61 fn get_finish_after(&self) -> Result<Option<u32>> {
65 current_ledger_object::get_field_optional(sfield::FinishAfter)
66 }
67
68 fn get_owner_node(&self) -> Result<u64> {
71 current_ledger_object::get_field(sfield::OwnerNode)
72 }
73
74 fn get_previous_txn_id(&self) -> Result<Hash256> {
76 current_ledger_object::get_field(sfield::PreviousTxnID)
77 }
78
79 fn get_previous_txn_lgr_seq(&self) -> Result<u32> {
82 current_ledger_object::get_field(sfield::PreviousTxnLgrSeq)
83 }
84
85 fn get_source_tag(&self) -> Result<Option<u32>> {
88 current_ledger_object::get_field_optional(sfield::SourceTag)
89 }
90
91 fn get_bytecode(&self) -> Result<Option<WasmBlob>> {
93 current_ledger_object::get_blob_field_optional(sfield::Bytecode)
94 }
95
96 fn get_data(&self) -> Result<ContractData> {
107 let mut data: [u8; XRPL_CONTRACT_DATA_SIZE] = [0; XRPL_CONTRACT_DATA_SIZE];
108
109 let result_code =
110 unsafe { home_le_field(sfield::Data.into(), data.as_mut_ptr(), data.len()) };
111
112 match result_code {
113 code if code >= 0 => Ok(ContractData {
114 data,
115 len: code as usize,
116 }),
117 code => Err(Error::from_code(code)),
118 }
119 }
120
121 fn update_current_escrow_data(data: ContractData) -> Result<()> {
133 let result_code = unsafe { set_data(data.data.as_ptr(), data.len) };
137 match_result_code(result_code, || ())
138 }
139}
140
141#[cfg(test)]
142mod tests {
143 use super::*;
144 use mockall::predicate::{always, eq};
145 use xrpl_common_stdlib::fields::decoder::FromLedger;
146 use xrpl_common_stdlib::host::error_codes::{FIELD_NOT_FOUND, INVALID_FIELD, SOME_ERROR};
147 use xrpl_common_stdlib::host::host_bindings_trait::MockHostBindings;
148 use xrpl_common_stdlib::sfield::SField;
149
150 fn expect_current_field<
151 T: FromLedger + Send + std::fmt::Debug + PartialEq + 'static,
152 const CODE: i32,
153 >(
154 mock: &mut MockHostBindings,
155 _field: SField<T, CODE>,
156 size: usize,
157 times: usize,
158 ) {
159 mock.expect_home_le_field()
160 .with(eq(CODE), always(), eq(size))
161 .times(times)
162 .returning(move |_, _, _| size as i32);
163 }
164
165 mod current_escrow_fields {
166 use super::*;
167 use crate::ledger_objects::current_escrow::CurrentEscrow;
168 use xrpl_common_stdlib::host::setup_mock;
169 use xrpl_common_stdlib::types::blob::CONDITION_BLOB_SIZE;
170 use xrpl_common_stdlib::types::blob::WASM_BLOB_SIZE;
171
172 #[test]
173 fn test_mandatory_fields_return_ok() {
174 let mut mock = MockHostBindings::new();
175
176 expect_current_field(&mut mock, sfield::Account, 20, 1);
178 mock.expect_home_le_field()
181 .with(eq::<i32>(sfield::Amount.into()), always(), eq(48))
182 .times(1)
183 .returning(|_, _, _| 8);
184 expect_current_field(&mut mock, sfield::Destination, 20, 1);
186 expect_current_field(&mut mock, sfield::OwnerNode, 8, 1);
188 expect_current_field(&mut mock, sfield::PreviousTxnID, 32, 1);
190 expect_current_field(&mut mock, sfield::PreviousTxnLgrSeq, 4, 1);
192 expect_current_field(&mut mock, sfield::Data, XRPL_CONTRACT_DATA_SIZE, 1);
194
195 let _guard = setup_mock(mock);
196
197 let escrow = CurrentEscrow;
198
199 assert!(escrow.get_account().is_ok());
201 assert!(escrow.get_amount().is_ok());
202 assert!(escrow.get_destination().is_ok());
203 assert!(escrow.get_owner_node().is_ok());
204 assert!(escrow.get_previous_txn_id().is_ok());
205 assert!(escrow.get_previous_txn_lgr_seq().is_ok());
206 assert!(escrow.get_data().is_ok());
207 }
208
209 #[test]
210 fn test_optional_fields_return_some() {
211 let mut mock = MockHostBindings::new();
212
213 expect_current_field(&mut mock, sfield::CancelAfter, 4, 1);
215 expect_current_field(&mut mock, sfield::Condition, CONDITION_BLOB_SIZE, 1);
217 expect_current_field(&mut mock, sfield::DestinationNode, 8, 1);
219 expect_current_field(&mut mock, sfield::DestinationTag, 4, 1);
221 expect_current_field(&mut mock, sfield::FinishAfter, 4, 1);
223 expect_current_field(&mut mock, sfield::SourceTag, 4, 1);
225 expect_current_field(&mut mock, sfield::Bytecode, WASM_BLOB_SIZE, 1);
227
228 let _guard = setup_mock(mock);
229
230 let escrow = CurrentEscrow;
231
232 assert!(escrow.get_cancel_after().unwrap().is_some());
234 assert!(escrow.get_condition().unwrap().is_some());
235 assert!(escrow.get_destination_node().unwrap().is_some());
236 assert!(escrow.get_destination_tag().unwrap().is_some());
237 assert!(escrow.get_finish_after().unwrap().is_some());
238 assert!(escrow.get_source_tag().unwrap().is_some());
239 assert!(escrow.get_bytecode().unwrap().is_some());
240 }
241
242 #[test]
243 fn test_optional_fields_return_none_when_field_not_found() {
244 let mut mock = MockHostBindings::new();
245
246 mock.expect_home_le_field()
248 .with(eq(sfield::CancelAfter), always(), eq(4))
249 .times(1)
250 .returning(|_, _, _| FIELD_NOT_FOUND);
251 mock.expect_home_le_field()
253 .with(eq(sfield::Condition), always(), eq(CONDITION_BLOB_SIZE))
254 .times(1)
255 .returning(|_, _, _| FIELD_NOT_FOUND);
256 mock.expect_home_le_field()
258 .with(eq(sfield::DestinationNode), always(), eq(8))
259 .times(1)
260 .returning(|_, _, _| FIELD_NOT_FOUND);
261 mock.expect_home_le_field()
263 .with(eq(sfield::DestinationTag), always(), eq(4))
264 .times(1)
265 .returning(|_, _, _| FIELD_NOT_FOUND);
266 mock.expect_home_le_field()
268 .with(eq(sfield::FinishAfter), always(), eq(4))
269 .times(1)
270 .returning(|_, _, _| FIELD_NOT_FOUND);
271 mock.expect_home_le_field()
273 .with(eq(sfield::SourceTag), always(), eq(4))
274 .times(1)
275 .returning(|_, _, _| FIELD_NOT_FOUND);
276 mock.expect_home_le_field()
278 .with(eq(sfield::Bytecode), always(), eq(WASM_BLOB_SIZE))
279 .times(1)
280 .returning(|_, _, _| 0);
281
282 let _guard = setup_mock(mock);
283
284 let escrow = CurrentEscrow;
285
286 assert!(escrow.get_cancel_after().unwrap().is_none());
288 assert!(escrow.get_condition().unwrap().is_none());
289 assert!(escrow.get_destination_node().unwrap().is_none());
290 assert!(escrow.get_destination_tag().unwrap().is_none());
291 assert!(escrow.get_finish_after().unwrap().is_none());
292 assert!(escrow.get_source_tag().unwrap().is_none());
293
294 let bytecode = escrow.get_bytecode().unwrap();
296 assert!(bytecode.is_some());
297 assert_eq!(bytecode.unwrap().len, 0);
298 }
299
300 #[test]
301 fn test_mandatory_fields_return_error_on_internal_error() {
302 let mut mock = MockHostBindings::new();
303
304 mock.expect_home_le_field()
306 .with(eq(sfield::Account), always(), eq(20))
307 .times(1)
308 .returning(|_, _, _| SOME_ERROR);
309
310 let _guard = setup_mock(mock);
311
312 let escrow = CurrentEscrow;
313 let result = escrow.get_account();
314
315 assert!(result.is_err());
316 assert_eq!(result.err().unwrap().code(), SOME_ERROR);
317 }
318
319 #[test]
320 fn test_get_data_returns_error_on_internal_error() {
321 let mut mock = MockHostBindings::new();
322
323 mock.expect_home_le_field()
324 .with(eq(sfield::Data), always(), eq(XRPL_CONTRACT_DATA_SIZE))
325 .times(1)
326 .returning(|_, _, _| SOME_ERROR);
327
328 let _guard = setup_mock(mock);
329
330 let escrow = CurrentEscrow;
331 let result = escrow.get_data();
332
333 assert!(result.is_err());
334 assert_eq!(result.err().unwrap().code(), SOME_ERROR);
335 }
336
337 #[test]
338 fn test_mandatory_fields_return_error_on_invalid_field() {
339 let mut mock = MockHostBindings::new();
340
341 mock.expect_home_le_field()
343 .with(eq(sfield::Account), always(), eq(20))
344 .times(1)
345 .returning(|_, _, _| INVALID_FIELD);
346
347 let _guard = setup_mock(mock);
348
349 let escrow = CurrentEscrow;
350 let result = escrow.get_account();
351
352 assert!(result.is_err());
353 assert_eq!(result.err().unwrap().code(), INVALID_FIELD);
354 }
355 }
356}