1#[cfg(not(target_arch = "wasm32"))]
2use crate::host::host_bindings_trait::{HostBindings, MockHostBindings};
3use std::cell::RefCell;
4
5#[cfg(all(any(test, feature = "test-host-bindings"), not(target_arch = "wasm32")))]
6pub struct MockGuard;
7
8#[cfg(all(any(test, feature = "test-host-bindings"), not(target_arch = "wasm32")))]
9impl Drop for MockGuard {
10 fn drop(&mut self) {
11 clear_mock_host_bindings();
12 }
13}
14
15#[cfg(all(any(test, feature = "test-host-bindings"), not(target_arch = "wasm32")))]
16pub fn setup_mock(mock: MockHostBindings) -> MockGuard {
17 set_mock_host_bindings(mock);
18 MockGuard
19}
20
21#[cfg(all(any(test, feature = "test-host-bindings"), not(target_arch = "wasm32")))]
23pub fn create_default_mock() -> MockHostBindings {
24 let mut mock = MockHostBindings::new();
25 apply_default_expectations(&mut mock);
26 mock
27}
28
29#[cfg(all(any(test, feature = "test-host-bindings"), not(target_arch = "wasm32")))]
38pub fn apply_default_expectations(mock: &mut MockHostBindings) {
39 mock.expect_ldgr_index()
41 .returning(|_, out_buff_len| out_buff_len as i32);
42 mock.expect_parent_ldgr_time()
43 .returning(|_, out_buff_len| out_buff_len as i32);
44 mock.expect_base_fee()
45 .returning(|_, out_buff_len| out_buff_len as i32);
46
47 mock.expect_parent_ldgr_hash()
49 .returning(|_, out_buff_len| out_buff_len as i32);
50 mock.expect_amendment_enabled()
51 .returning(|_, amendment_len| amendment_len as i32);
52 mock.expect_cache_le()
53 .returning(|_, id_len, _| id_len as i32);
54 const STI_AMOUNT: i32 = 6;
61 mock.expect_tx_field().returning(|field, _, out_buff_len| {
62 if field >> 16 == STI_AMOUNT {
63 8
64 } else {
65 out_buff_len as i32
66 }
67 });
68 mock.expect_home_le_field()
69 .returning(|field, _, out_buff_len| {
70 if field >> 16 == STI_AMOUNT {
71 8
72 } else {
73 out_buff_len as i32
74 }
75 });
76 mock.expect_le_field()
77 .returning(|_, field, _, out_buff_len| {
78 if field >> 16 == STI_AMOUNT {
79 8
80 } else {
81 out_buff_len as i32
82 }
83 });
84 mock.expect_tx_inner()
85 .returning(|_, _, _, out_buff_len| out_buff_len as i32);
86 mock.expect_home_le_inner()
87 .returning(|_, _, _, out_buff_len| out_buff_len as i32);
88 mock.expect_le_inner()
89 .returning(|_, _, _, _, out_buff_len| out_buff_len as i32);
90
91 mock.expect_tx_arr_len().returning(|_| 0);
93 mock.expect_home_le_arr_len().returning(|_| 0);
94 mock.expect_le_arr_len().returning(|_, _| 0);
95 mock.expect_tx_inner_arr_len().returning(|_, _| 0);
96 mock.expect_home_le_inner_arr_len()
98 .returning(|_, locator_len| locator_len as i32);
99 mock.expect_le_inner_arr_len()
100 .returning(|_, _, locator_len| locator_len as i32);
101
102 mock.expect_set_data()
104 .returning(|_, data_len| data_len as i32);
105 mock.expect_sha512_half()
106 .returning(|_, _, _, out_buff_len| out_buff_len as i32);
107 mock.expect_check_sig().returning(|_, _, _, _, _, _| 0);
108
109 mock.expect_accountroot_id()
111 .returning(|_, _, _, out_buff_len| out_buff_len as i32);
112 mock.expect_amm_id()
113 .returning(|_, _, _, _, _, out_buff_len| out_buff_len as i32);
114 mock.expect_check_id()
115 .returning(|_, _, _, _, _, out_buff_len| out_buff_len as i32);
116 mock.expect_credential_id()
117 .returning(|_, _, _, _, _, _, _, out_buff_len| out_buff_len as i32);
118 mock.expect_delegate_id()
119 .returning(|_, _, _, _, _, out_buff_len| out_buff_len as i32);
120 mock.expect_deposit_preauth_id()
121 .returning(|_, _, _, _, _, out_buff_len| out_buff_len as i32);
122 mock.expect_did_id()
123 .returning(|_, _, _, out_buff_len| out_buff_len as i32);
124 mock.expect_escrow_id()
125 .returning(|_, _, _, _, _, out_buff_len| out_buff_len as i32);
126 mock.expect_trustline_id()
127 .returning(|_, _, _, _, _, _, _, out_buff_len| out_buff_len as i32);
128 mock.expect_mpt_issuance_id()
129 .returning(|_, _, _, _, _, out_buff_len| out_buff_len as i32);
130 mock.expect_mptoken_id()
131 .returning(|_, _, _, _, _, out_buff_len| out_buff_len as i32);
132 mock.expect_nft_offer_id()
133 .returning(|_, _, _, _, _, out_buff_len| out_buff_len as i32);
134 mock.expect_offer_id()
135 .returning(|_, _, _, _, _, out_buff_len| out_buff_len as i32);
136 mock.expect_oracle_id()
137 .returning(|_, _, _, _, _, out_buff_len| out_buff_len as i32);
138 mock.expect_paychan_id()
139 .returning(|_, _, _, _, _, _, _, out_buff_len| out_buff_len as i32);
140 mock.expect_permissioned_domain_id()
141 .returning(|_, _, _, _, _, out_buff_len| out_buff_len as i32);
142 mock.expect_signers_id()
143 .returning(|_, _, _, out_buff_len| out_buff_len as i32);
144 mock.expect_ticket_id()
145 .returning(|_, _, _, _, _, out_buff_len| out_buff_len as i32);
146 mock.expect_vault_id()
147 .returning(|_, _, _, _, _, out_buff_len| out_buff_len as i32);
148
149 mock.expect_nft_uri()
151 .returning(|_, _, _, _, _, out_buff_len| out_buff_len as i32);
152 mock.expect_nft_issuer()
153 .returning(|_, _, _, out_buff_len| out_buff_len as i32);
154 mock.expect_nft_taxon()
155 .returning(|_, _, _, out_buff_len| out_buff_len as i32);
156 mock.expect_nft_flags()
157 .returning(|_, nft_id_len| nft_id_len as i32);
158 mock.expect_nft_xfer_fee()
159 .returning(|_, nft_id_len| nft_id_len as i32);
160 mock.expect_nft_serial()
161 .returning(|_, _, _, out_buff_len| out_buff_len as i32);
162
163 mock.expect_float_from_int()
165 .returning(|_, _, out_buff_len, _| out_buff_len as i32);
166 mock.expect_float_from_uint()
167 .returning(|_, _, _, out_buff_len, _| out_buff_len as i32);
168 mock.expect_float_from_mant_exp()
169 .returning(|_, _, _, out_buff_len, _| out_buff_len as i32);
170 mock.expect_float_from_stamount()
171 .returning(|_, _, _, out_buff_len, _| out_buff_len as i32);
172 mock.expect_float_from_stnumber()
173 .returning(|_, _, _, out_buff_len, _| out_buff_len as i32);
174 mock.expect_float_to_int()
175 .returning(|_, _, _, out_buff_len, _| out_buff_len as i32);
176 mock.expect_float_to_mant_exp()
177 .returning(|_, _, _, _, _, _| 8);
178 mock.expect_float_cmp().returning(|_, _, _, _| 0);
179 mock.expect_float_add()
180 .returning(|_, _, _, _, _, out_buff_len, _| out_buff_len as i32);
181 mock.expect_float_sub()
182 .returning(|_, _, _, _, _, out_buff_len, _| out_buff_len as i32);
183 mock.expect_float_mult()
184 .returning(|_, _, _, _, _, out_buff_len, _| out_buff_len as i32);
185 mock.expect_float_div()
186 .returning(|_, _, _, _, _, out_buff_len, _| out_buff_len as i32);
187 mock.expect_float_pow()
188 .returning(|_, _, _, _, out_buff_len, _| out_buff_len as i32);
189
190 mock.expect_trace().returning(|_, _, _, _, _| ());
192}
193
194#[cfg(all(any(test, feature = "test-host-bindings"), not(target_arch = "wasm32")))]
196thread_local! {
197 static MOCK_STATE: RefCell<Option<MockHostBindings>> = RefCell::new(Some(create_default_mock()));
198}
199
200#[cfg(all(any(test, feature = "test-host-bindings"), not(target_arch = "wasm32")))]
202pub fn set_mock_host_bindings(mock: MockHostBindings) {
203 MOCK_STATE.with(|state| {
204 *state.borrow_mut() = Some(mock);
205 });
206}
207
208#[cfg(all(any(test, feature = "test-host-bindings"), not(target_arch = "wasm32")))]
209pub fn clear_mock_host_bindings() {
210 MOCK_STATE.with(|state| {
211 *state.borrow_mut() = None;
212 });
213}
214
215#[cfg(all(any(test, feature = "test-host-bindings"), not(target_arch = "wasm32")))]
216macro_rules! export_host_functions {
219 ($(
220 $(#[$attr:meta])*
221 fn $name:ident($($param:ident: $param_ty:ty),*) -> $ret:ty;
222 )*) => {
223 $(
224 #[allow(clippy::too_many_arguments)]
225 #[allow(clippy::missing_safety_doc)]
226 $(#[$attr])*
227 pub unsafe fn $name($($param: $param_ty),*) -> $ret {
228 MOCK_STATE.with(|state| {
229 let mock = state.borrow();
232 let mock_ref = mock.as_ref().expect("MockHostBindings not initialized");
233 unsafe { mock_ref.$name($($param),*) }
234 })
235 }
236 )*
237 };
238}
239
240export_host_functions! {
246 fn ldgr_index(out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
248 fn parent_ldgr_time(out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
249 fn parent_ldgr_hash(out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
250 fn base_fee(out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
251 fn amendment_enabled(amendment_ptr: *const u8, amendment_len: usize) -> i32;
252 fn cache_le(id_ptr: *const u8, id_len: usize, cache_num: i32) -> i32;
253 fn tx_field(field: i32, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
254 fn home_le_field(field: i32, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
255 fn le_field(cache_num: i32, field: i32, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
256 fn tx_inner(locator_ptr: *const u8, locator_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
257 fn home_le_inner(locator_ptr: *const u8, locator_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
258 fn le_inner(cache_num: i32, locator_ptr: *const u8, locator_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
259 fn tx_arr_len(field: i32) -> i32;
260 fn home_le_arr_len(field: i32) -> i32;
261 fn le_arr_len(cache_num: i32, field: i32) -> i32;
262 fn tx_inner_arr_len(locator_ptr: *const u8, locator_len: usize) -> i32;
263 fn home_le_inner_arr_len(locator_ptr: *const u8, locator_len: usize) -> i32;
264 fn le_inner_arr_len(cache_num: i32, locator_ptr: *const u8, locator_len: usize) -> i32;
265
266 fn set_data(data_ptr: *const u8, data_len: usize) -> i32;
268
269 fn sha512_half(data_ptr: *const u8, data_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
271 fn check_sig(message_ptr: *const u8, message_len: usize, signature_ptr: *const u8, signature_len: usize, pubkey_ptr: *const u8, pubkey_len: usize) -> i32;
272 fn accountroot_id(account_ptr: *const u8, account_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
273 fn amm_id(issue1_ptr: *const u8, issue1_len: usize, issue2_ptr: *const u8, issue2_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
274 fn check_id(account_ptr: *const u8, account_len: usize, sequence_ptr: *const u8, sequence_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
275 fn credential_id(subject_ptr: *const u8, subject_len: usize, issuer_ptr: *const u8, issuer_len: usize, cred_type_ptr: *const u8, cred_type_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
276 fn delegate_id(account_ptr: *const u8, account_len: usize, authorize_ptr: *const u8, authorize_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
277 fn deposit_preauth_id(account_ptr: *const u8, account_len: usize, authorize_ptr: *const u8, authorize_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
278 fn did_id(account_ptr: *const u8, account_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
279 fn escrow_id(account_ptr: *const u8, account_len: usize, sequence_ptr: *const u8, sequence_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
280 fn trustline_id(account1_ptr: *const u8, account1_len: usize, account2_ptr: *const u8, account2_len: usize, currency_ptr: *const u8, currency_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
281 fn mpt_issuance_id(issuer_ptr: *const u8, issuer_len: usize, sequence_ptr: *const u8, sequence_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
282 fn mptoken_id(mptid_ptr: *const u8, mptid_len: usize, holder_ptr: *const u8, holder_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
283 fn nft_offer_id(account_ptr: *const u8, account_len: usize, sequence_ptr: *const u8, sequence_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
284 fn offer_id(account_ptr: *const u8, account_len: usize, sequence_ptr: *const u8, sequence_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
285 fn oracle_id(account_ptr: *const u8, account_len: usize, document_id_ptr: *const u8, document_id_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
286 fn paychan_id(account_ptr: *const u8, account_len: usize, destination_ptr: *const u8, destination_len: usize, sequence_ptr: *const u8, sequence_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
287 fn permissioned_domain_id(account_ptr: *const u8, account_len: usize, sequence_ptr: *const u8, sequence_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
288 fn signers_id(account_ptr: *const u8, account_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
289 fn ticket_id(account_ptr: *const u8, account_len: usize, sequence_ptr: *const u8, sequence_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
290 fn vault_id(account_ptr: *const u8, account_len: usize, sequence_ptr: *const u8, sequence_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
291
292 fn nft_uri(account_ptr: *const u8, account_len: usize, nft_id_ptr: *const u8, nft_id_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
294 fn nft_issuer(nft_id_ptr: *const u8, nft_id_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
295 fn nft_taxon(nft_id_ptr: *const u8, nft_id_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
296 fn nft_flags(nft_id_ptr: *const u8, nft_id_len: usize) -> i32;
297 fn nft_xfer_fee(nft_id_ptr: *const u8, nft_id_len: usize) -> i32;
298 fn nft_serial(nft_id_ptr: *const u8, nft_id_len: usize, out_buff_ptr: *mut u8, out_buff_len: usize) -> i32;
299
300 fn float_from_int(in_int: i64, out_buff: *mut u8, out_buff_len: usize, rounding_mode: i32) -> i32;
302 fn float_from_uint(in_uint_ptr: *const u8, in_uint_len: usize, out_buff: *mut u8, out_buff_len: usize, rounding_mode: i32) -> i32;
303 fn float_from_mant_exp(mantissa: i64, exponent: i32, out_buff: *mut u8, out_buff_len: usize, rounding_mode: i32) -> i32;
304 fn float_from_stamount(in_buff: *const u8, in_buff_len: usize, out_buff: *mut u8, out_buff_len: usize, rounding_mode: i32) -> i32;
305 fn float_from_stnumber(in_buff: *const u8, in_buff_len: usize, out_buff: *mut u8, out_buff_len: usize, rounding_mode: i32) -> i32;
306 fn float_to_int(in_buff: *const u8, in_buff_len: usize, out_buff: *mut u8, out_buff_len: usize, rounding_mode: i32) -> i32;
307 fn float_to_mant_exp(in_buff: *const u8, in_buff_len: usize, mant_buff: *mut u8, mant_buff_len: usize, exp_buff: *mut u8, exp_buff_len: usize) -> i32;
308 fn float_cmp(in_buff1: *const u8, in_buff1_len: usize, in_buff2: *const u8, in_buff2_len: usize) -> i32;
309 fn float_add(in_buff1: *const u8, in_buff1_len: usize, in_buff2: *const u8, in_buff2_len: usize, out_buff: *mut u8, out_buff_len: usize, rounding_mode: i32) -> i32;
310 fn float_sub(in_buff1: *const u8, in_buff1_len: usize, in_buff2: *const u8, in_buff2_len: usize, out_buff: *mut u8, out_buff_len: usize, rounding_mode: i32) -> i32;
311 fn float_mult(in_buff1: *const u8, in_buff1_len: usize, in_buff2: *const u8, in_buff2_len: usize, out_buff: *mut u8, out_buff_len: usize, rounding_mode: i32) -> i32;
312 fn float_div(in_buff1: *const u8, in_buff1_len: usize, in_buff2: *const u8, in_buff2_len: usize, out_buff: *mut u8, out_buff_len: usize, rounding_mode: i32) -> i32;
313 fn float_pow(in_buff: *const u8, in_buff_len: usize, pow: i32, out_buff: *mut u8, out_buff_len: usize, rounding_mode: i32) -> i32;
314
315 fn trace(msg_read_ptr: *const u8, msg_read_len: usize, data_type: i32, data_read_ptr: *const u8, data_read_len: usize) -> ();
317
318}
319
320#[cfg(test)]
321mod tests {
322 use super::*;
323 use crate::host::trace::TraceDataType;
324
325 #[test]
326 fn test_ledger_functions_with_mock() {
327 let mut mock = MockHostBindings::new();
328
329 mock.expect_ldgr_index().times(1).returning(|_, _| 12345);
331 mock.expect_parent_ldgr_time()
332 .times(1)
333 .returning(|_, _| 1234567890);
334 mock.expect_base_fee().times(1).returning(|_, _| 10);
335
336 set_mock_host_bindings(mock);
338
339 let mut buffer = [0u8; 32];
341 unsafe {
342 assert_eq!(ldgr_index(buffer.as_mut_ptr(), buffer.len()), 12345);
343 assert_eq!(
344 parent_ldgr_time(buffer.as_mut_ptr(), buffer.len()),
345 1234567890
346 );
347 assert_eq!(base_fee(buffer.as_mut_ptr(), buffer.len()), 10);
348 }
349
350 clear_mock_host_bindings();
352 }
353
354 #[test]
355 fn test_buffer_operations_with_mock() {
356 let mut mock = MockHostBindings::new();
357
358 mock.expect_parent_ldgr_hash()
360 .times(1)
361 .returning(|out_buff_ptr, out_buff_len| {
362 if out_buff_len >= 32 {
363 unsafe {
364 for i in 0..32 {
366 *out_buff_ptr.add(i) = (i * 2) as u8;
367 }
368 }
369 32 } else {
371 -1 }
373 });
374
375 let mut buffer = [0u8; 32];
377 unsafe {
378 let result = mock.parent_ldgr_hash(buffer.as_mut_ptr(), buffer.len());
379 assert_eq!(result, 32);
380
381 for (i, _) in buffer.iter().enumerate() {
383 assert_eq!(buffer[i], (i * 2) as u8);
384 }
385 }
386 }
387
388 #[test]
389 fn test_trace_functions_with_mock() {
390 let mut mock = MockHostBindings::new();
391
392 mock.expect_trace()
393 .times(2)
394 .returning(|_msg_ptr, _msg_len, _data_type, _data_ptr, _data_len| ());
395
396 let message = b"Test message";
397 let data = b"Test data";
398 let number = 42i64.to_le_bytes();
399
400 unsafe {
401 mock.trace(
402 message.as_ptr(),
403 message.len(),
404 TraceDataType::AsText as i32,
405 data.as_ptr(),
406 data.len(),
407 );
408
409 mock.trace(
410 message.as_ptr(),
411 message.len(),
412 TraceDataType::Int64 as i32,
413 number.as_ptr(),
414 number.len(),
415 );
416 }
417 }
418
419 #[test]
420 fn test_id_functions_with_mock() {
421 let mut mock = MockHostBindings::new();
422
423 mock.expect_accountroot_id().times(1).returning(
425 |_account_ptr, _account_len, out_buff_ptr, out_buff_len| {
426 if out_buff_len >= 32 {
427 unsafe {
428 for i in 0..32 {
430 *out_buff_ptr.add(i) = 0xAA;
431 }
432 }
433 32
434 } else {
435 -1
436 }
437 },
438 );
439
440 let account = [0u8; 20]; let mut id_buffer = [0u8; 32];
443
444 unsafe {
445 let result = mock.accountroot_id(
446 account.as_ptr(),
447 account.len(),
448 id_buffer.as_mut_ptr(),
449 id_buffer.len(),
450 );
451
452 assert_eq!(result, 32);
453 assert_eq!(id_buffer, [0xAA; 32]);
454 }
455 }
456
457 #[test]
458 fn test_error_conditions_with_mock() {
459 let mut mock = MockHostBindings::new();
460
461 mock.expect_ldgr_index().times(1).returning(|_, _| -1); mock.expect_parent_ldgr_hash()
465 .times(1)
466 .returning(|_out_buff_ptr, _out_buff_len| -2); unsafe {
469 let mut buffer = [0u8; 32];
471 assert_eq!(mock.ldgr_index(buffer.as_mut_ptr(), buffer.len()), -1);
472
473 let mut small_buffer = [0u8; 16]; let result = mock.parent_ldgr_hash(small_buffer.as_mut_ptr(), small_buffer.len());
475 assert_eq!(result, -2);
476 }
477 }
478
479 #[test]
480 fn test_generic_function_with_mock() {
481 fn get_ledger_info<H: HostBindings>(host: &H) -> (i32, i32, i32) {
483 let mut buffer = [0u8; 32];
484 unsafe {
485 let sqn = host.ldgr_index(buffer.as_mut_ptr(), buffer.len());
486 let time = host.parent_ldgr_time(buffer.as_mut_ptr(), buffer.len());
487 let fee = host.base_fee(buffer.as_mut_ptr(), buffer.len());
488 (sqn, time, fee)
489 }
490 }
491
492 let mut mock = MockHostBindings::new();
493
494 mock.expect_ldgr_index().returning(|_, _| 999);
495 mock.expect_parent_ldgr_time().returning(|_, _| 888);
496 mock.expect_base_fee().returning(|_, _| 777);
497
498 let (sqn, time, fee) = get_ledger_info(&mock);
499 assert_eq!(sqn, 999);
500 assert_eq!(time, 888);
501 assert_eq!(fee, 777);
502 }
503}