1#![allow(unused)]
2use crate::data_provider::{
3 DataProvider, HostError, RippledRoundingMode, XRPL_CONTRACT_DATA_SIZE, error_code_to_string,
4 unpack_locator,
5};
6use crate::decoding::{
7 _deserialize_issued_currency_amount, _serialize_issued_currency_value, ACCOUNT_ID_LEN,
8 CURRENCY_LEN, MPT_ID_LEN, decode_account_id,
9};
10use crate::hashing::{HASH256_LEN, LedgerNameSpace, index_hash, sha512_half};
11use crate::mock_data::{DataSource, Keylet};
12use bigdecimal::num_bigint::{BigInt, ToBigInt};
13use bigdecimal::num_traits::real::Real;
14use bigdecimal::{BigDecimal, ToPrimitive};
15use hex::decode;
16use log::{debug, warn};
17use num_traits::FromPrimitive;
18use wamr_rust_sdk::sys::{
19 wasm_exec_env_t, wasm_runtime_get_function_attachment, wasm_runtime_get_module_inst,
20 wasm_runtime_validate_native_addr,
21};
22use xrpl::core::addresscodec::utils::encode_base58;
23use xrpl_wasm_std::core::types::amount::token_amount::TokenAmount;
24use xrpld_number::{
25 FLOAT_NEGATIVE_ONE, FLOAT_ONE, Number, RoundingMode as NumberRoundingMode, XrplIouValue,
26};
27
28struct RoundingModeGuard {
31 previous_mode: Option<NumberRoundingMode>,
32}
33
34impl RoundingModeGuard {
35 fn new(mode: NumberRoundingMode) -> Self {
37 let previous_mode = Number::get_rounding_mode();
38 Number::set_rounding_mode(mode);
39 Self {
40 previous_mode: Some(previous_mode),
41 }
42 }
43
44 fn noop() -> Self {
46 Self {
47 previous_mode: None,
48 }
49 }
50}
51
52impl Drop for RoundingModeGuard {
53 fn drop(&mut self) {
54 if let Some(mode) = self.previous_mode {
55 Number::set_rounding_mode(mode);
56 }
57 }
58}
59
60fn set_rounding_mode_from_param(rounding_mode: i32) -> RoundingModeGuard {
63 if (0..=3).contains(&rounding_mode) {
64 let mode = match rounding_mode {
65 0 => NumberRoundingMode::ToNearest,
66 1 => NumberRoundingMode::TowardsZero,
67 2 => NumberRoundingMode::Downward,
68 3 => NumberRoundingMode::Upward,
69 _ => NumberRoundingMode::ToNearest, };
71 RoundingModeGuard::new(mode)
72 } else {
73 RoundingModeGuard::noop()
74 }
75}
76
77const MAX_WASM_PARAM_LENGTH: usize = 1024;
78
79pub fn get_dp(env: wasm_exec_env_t) -> &'static mut DataProvider {
80 unsafe { &mut *(wasm_runtime_get_function_attachment(env) as *mut DataProvider) }
81}
82
83fn get_data(in_buf_ptr: *const u8, in_buf_len: usize) -> Vec<u8> {
84 let mut buffer = vec![0u8; in_buf_len];
85 unsafe {
86 std::ptr::copy_nonoverlapping(in_buf_ptr, buffer.as_mut_ptr(), in_buf_len);
87 }
88 buffer
89}
90
91fn get_keylet(in_buf_ptr: *const u8, in_buf_len: usize) -> Keylet {
92 get_data(in_buf_ptr, in_buf_len)
93}
94
95fn set_data(dp_res: i32, out_buf_ptr: *mut u8, data_to_write: Vec<u8>) {
96 if dp_res > 0 {
97 unsafe {
98 std::ptr::copy_nonoverlapping(data_to_write.as_ptr(), out_buf_ptr, data_to_write.len());
99 }
100 }
101}
102
103pub fn get_ledger_sqn(env: wasm_exec_env_t) -> i32 {
104 let data_provider = get_dp(env);
105 data_provider.get_ledger_sqn()
106}
107
108pub fn get_parent_ledger_time(
109 env: wasm_exec_env_t,
110 out_buf_ptr: *mut u8,
111 out_buf_cap: usize,
112) -> i32 {
113 let data_provider = get_dp(env);
114 data_provider.get_parent_ledger_time()
115}
116
117pub fn get_parent_ledger_hash(
118 env: wasm_exec_env_t,
119 out_buf_ptr: *mut u8,
120 out_buf_cap: usize,
121) -> i32 {
122 let data_provider = get_dp(env);
123 let dp_res = data_provider.get_parent_ledger_hash(out_buf_cap);
124 set_data(dp_res.0, out_buf_ptr, dp_res.1);
125 dp_res.0
126}
127
128pub fn cache_ledger_obj(
129 env: wasm_exec_env_t,
130 in_buf_ptr: *const u8,
131 in_buf_cap: usize,
132 cache_num: i32,
133) -> i32 {
134 let data_provider = get_dp(env);
135 let keylet = get_keylet(in_buf_ptr, in_buf_cap);
136 data_provider.slot_set(keylet, cache_num as usize)
137}
138
139pub fn get_tx_field(
140 env: wasm_exec_env_t,
141 field: i32,
142 out_buf_ptr: *mut u8,
143 out_buf_cap: usize,
144) -> i32 {
145 let data_provider = get_dp(env);
146 let dp_res = data_provider.get_field_value(DataSource::Tx, vec![field], out_buf_cap);
147 set_data(dp_res.0, out_buf_ptr, dp_res.1);
148 dp_res.0
149}
150
151pub fn get_current_ledger_obj_field(
152 env: wasm_exec_env_t,
153 field: i32,
154 out_buf_ptr: *mut u8,
155 out_buf_cap: usize,
156) -> i32 {
157 let data_provider = get_dp(env);
158 let dp_res =
159 data_provider.get_field_value(DataSource::CurrentLedgerObj, vec![field], out_buf_cap);
160 set_data(dp_res.0, out_buf_ptr, dp_res.1);
161 dp_res.0
162}
163
164pub fn get_ledger_obj_field(
165 env: wasm_exec_env_t,
166 slot: i32,
167 field: i32,
168 out_buf_ptr: *mut u8,
169 out_buf_cap: usize,
170) -> i32 {
171 let data_provider = get_dp(env);
172 let keylet = match data_provider.slot_get(slot as usize) {
173 None => return HostError::EmptySlot as i32,
174 Some(key) => key.clone(),
175 };
176 let dp_res = data_provider.get_field_value(
177 DataSource::KeyletLedgerObj(keylet),
178 vec![field],
179 out_buf_cap,
180 );
181
182 set_data(dp_res.0, out_buf_ptr, dp_res.1);
183 dp_res.0
184}
185
186pub fn get_tx_nested_field(
187 env: wasm_exec_env_t,
188 in_buf_ptr: *const u8,
189 in_buf_len: usize,
190 out_buf_ptr: *mut u8,
191 out_buf_cap: usize,
192) -> i32 {
193 let data_provider = get_dp(env);
194 let data = get_data(in_buf_ptr, in_buf_len);
195 let idx_fields: Vec<i32> = match unpack_locator(data) {
196 Ok(fields) => fields,
197 Err(host_err) => return host_err as i32,
198 };
199
200 let dp_res = data_provider.get_field_value(DataSource::Tx, idx_fields, out_buf_cap);
201 set_data(dp_res.0, out_buf_ptr, dp_res.1);
202 dp_res.0
203}
204
205pub fn get_current_ledger_obj_nested_field(
206 env: wasm_exec_env_t,
207 in_buf_ptr: *const u8,
208 in_buf_len: usize,
209 out_buf_ptr: *mut u8,
210 out_buf_cap: usize,
211) -> i32 {
212 let data_provider = get_dp(env);
213 let data = get_data(in_buf_ptr, in_buf_len);
214 let idx_fields: Vec<i32> = match unpack_locator(data) {
215 Ok(fields) => fields,
216 Err(host_err) => return host_err as i32,
217 };
218
219 let dp_res =
220 data_provider.get_field_value(DataSource::CurrentLedgerObj, idx_fields, out_buf_cap);
221 set_data(dp_res.0, out_buf_ptr, dp_res.1);
222 dp_res.0
223}
224
225pub fn get_ledger_obj_nested_field(
226 env: wasm_exec_env_t,
227 slot: i32,
228 in_buf_ptr: *const u8,
229 in_buf_len: usize,
230 out_buf_ptr: *mut u8,
231 out_buf_cap: usize,
232) -> i32 {
233 let data_provider = get_dp(env);
234 let keylet = match data_provider.slot_get(slot as usize) {
235 None => return HostError::EmptySlot as i32,
236 Some(key) => key.clone(),
237 };
238
239 let data = get_data(in_buf_ptr, in_buf_len);
240 let idx_fields: Vec<i32> = match unpack_locator(data) {
241 Ok(fields) => fields,
242 Err(host_err) => return host_err as i32,
243 };
244
245 let dp_res =
246 data_provider.get_field_value(DataSource::KeyletLedgerObj(keylet), idx_fields, out_buf_cap);
247 set_data(dp_res.0, out_buf_ptr, dp_res.1);
248 dp_res.0
249}
250
251pub fn get_tx_array_len(env: wasm_exec_env_t, field: i32) -> i32 {
252 let data_provider = get_dp(env);
253 data_provider.get_array_len(DataSource::Tx, vec![field])
254}
255pub fn get_current_ledger_obj_array_len(env: wasm_exec_env_t, field: i32) -> i32 {
256 let data_provider = get_dp(env);
257 data_provider.get_array_len(DataSource::CurrentLedgerObj, vec![field])
258}
259pub fn get_ledger_obj_array_len(env: wasm_exec_env_t, slot: i32, field: i32) -> i32 {
260 let data_provider = get_dp(env);
261 let keylet = match data_provider.slot_get(slot as usize) {
262 None => return HostError::EmptySlot as i32,
263 Some(key) => key.clone(),
264 };
265 data_provider.get_array_len(DataSource::KeyletLedgerObj(keylet), vec![field])
266}
267pub fn get_tx_nested_array_len(
268 env: wasm_exec_env_t,
269 in_buf_ptr: *const u8,
270 in_buf_len: usize,
271) -> i32 {
272 let data_provider = get_dp(env);
273 let data = get_data(in_buf_ptr, in_buf_len);
274 let idx_fields: Vec<i32> = match unpack_locator(data) {
275 Ok(fields) => fields,
276 Err(host_err) => return host_err as i32,
277 };
278 data_provider.get_array_len(DataSource::Tx, idx_fields)
279}
280pub fn get_current_ledger_obj_nested_array_len(
281 env: wasm_exec_env_t,
282 in_buf_ptr: *const u8,
283 in_buf_len: usize,
284) -> i32 {
285 let data_provider = get_dp(env);
286 let data = get_data(in_buf_ptr, in_buf_len);
287 let idx_fields: Vec<i32> = match unpack_locator(data) {
288 Ok(fields) => fields,
289 Err(host_err) => return host_err as i32,
290 };
291 data_provider.get_array_len(DataSource::CurrentLedgerObj, idx_fields)
292}
293pub fn get_ledger_obj_nested_array_len(
294 env: wasm_exec_env_t,
295 slot: i32,
296 in_buf_ptr: *const u8,
297 in_buf_len: usize,
298) -> i32 {
299 let data_provider = get_dp(env);
300 let keylet = match data_provider.slot_get(slot as usize) {
301 None => return HostError::EmptySlot as i32,
302 Some(key) => key.clone(),
303 };
304
305 let data = get_data(in_buf_ptr, in_buf_len);
306 let idx_fields: Vec<i32> = match unpack_locator(data) {
307 Ok(fields) => fields,
308 Err(host_err) => return host_err as i32,
309 };
310
311 data_provider.get_array_len(DataSource::KeyletLedgerObj(keylet), idx_fields)
312}
313pub fn update_data(env: wasm_exec_env_t, in_buf_ptr: *const u8, in_buf_len: usize) -> i32 {
314 let data_provider = get_dp(env);
315 if in_buf_len > XRPL_CONTRACT_DATA_SIZE {
316 return HostError::DataFieldTooLarge as i32;
317 }
318 let data = get_data(in_buf_ptr, in_buf_len);
319 data_provider.set_current_ledger_obj_data(data);
320 0
321}
322pub fn compute_sha512_half(
323 _env: wasm_exec_env_t,
324 in_buf_ptr: *const u8,
325 in_buf_len: usize,
326 out_buf_ptr: *mut u8,
327 out_buf_cap: usize,
328) -> i32 {
329 if HASH256_LEN > out_buf_cap {
330 return HostError::BufferTooSmall as i32;
331 }
332 if in_buf_len > MAX_WASM_PARAM_LENGTH {
333 return HostError::DataFieldTooLarge as i32;
334 }
335 let data = get_data(in_buf_ptr, in_buf_len);
336 let hash_half = sha512_half(&data);
337 set_data(hash_half.len() as i32, out_buf_ptr, hash_half);
338 HASH256_LEN as i32
339}
340
341pub fn account_keylet(
342 _env: wasm_exec_env_t,
343 account_buf_ptr: *const u8,
344 account_buf_len: usize,
345 out_buf_ptr: *mut u8,
346 out_buf_cap: usize,
347) -> i32 {
348 if HASH256_LEN > out_buf_cap {
349 return HostError::BufferTooSmall as i32;
350 }
351 let data = get_data(account_buf_ptr, account_buf_len);
352 if ACCOUNT_ID_LEN != data.len() {
353 return HostError::InvalidAccount as i32;
354 }
355 let keylet_hash = index_hash(LedgerNameSpace::Account, &data);
356 set_data(keylet_hash.len() as i32, out_buf_ptr, keylet_hash);
359 HASH256_LEN as i32
360}
361
362struct Issue {
363 currency: [u8; CURRENCY_LEN],
364 issuer: [u8; ACCOUNT_ID_LEN],
365}
366
367impl PartialEq for Issue {
368 fn eq(&self, other: &Self) -> bool {
369 self.currency == other.currency && self.issuer == other.issuer
370 }
371}
372
373impl Eq for Issue {}
374
375impl PartialOrd for Issue {
376 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
377 Some(self.cmp(other))
378 }
379}
380
381impl Ord for Issue {
382 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
383 match self.currency.cmp(&other.currency) {
384 std::cmp::Ordering::Equal => self.issuer.cmp(&other.issuer),
385 ord => ord,
386 }
387 }
388}
389
390fn parse_asset(asset_data: &[u8]) -> Result<Issue, HostError> {
391 if asset_data.len() == MPT_ID_LEN {
393 return Err(HostError::InvalidParams);
396 }
397
398 if asset_data.len() == CURRENCY_LEN {
400 return Ok(Issue {
403 currency: asset_data.try_into().unwrap(),
404 issuer: [0u8; ACCOUNT_ID_LEN],
405 });
406 }
407
408 if asset_data.len() == CURRENCY_LEN + ACCOUNT_ID_LEN {
410 let currency = &asset_data[..CURRENCY_LEN];
412 let issuer = &asset_data[CURRENCY_LEN..];
413 let is_native = currency.iter().all(|&b| b == 0) && issuer.iter().all(|&b| b == 0);
416 if is_native {
417 return Err(HostError::InvalidParams);
418 }
419 return Ok(Issue {
420 currency: currency.try_into().unwrap(),
421 issuer: issuer.try_into().unwrap(),
422 });
423 }
424
425 Err(HostError::InvalidParams)
426}
427
428pub fn amm_keylet(
429 _env: wasm_exec_env_t,
430 asset1_ptr: *const u8,
431 asset1_len: usize,
432 asset2_ptr: *const u8,
433 asset2_len: usize,
434 out_buf_ptr: *mut u8,
435 out_buf_cap: usize,
436) -> i32 {
437 if HASH256_LEN > out_buf_cap {
438 return HostError::BufferTooSmall as i32;
439 }
440 let asset1 = match parse_asset(&get_data(asset1_ptr, asset1_len)) {
441 Ok(a) => a,
442 Err(e) => return e as i32,
443 };
444 let asset2 = match parse_asset(&get_data(asset2_ptr, asset2_len)) {
445 Ok(a) => a,
446 Err(e) => return e as i32,
447 };
448 let (min_asset, mut max_asset) = if asset1 <= asset2 {
450 (asset1, asset2)
451 } else {
452 (asset2, asset1)
453 };
454 let mut data = min_asset.issuer.to_vec();
455 data.extend_from_slice(&min_asset.currency);
456 data.extend_from_slice(&max_asset.issuer);
457 data.extend_from_slice(&max_asset.currency);
458 let keylet_hash = index_hash(LedgerNameSpace::Amm, &data);
459 set_data(keylet_hash.len() as i32, out_buf_ptr, keylet_hash);
460 HASH256_LEN as i32
461}
462
463pub fn check_keylet(
464 _env: wasm_exec_env_t,
465 account_buf_ptr: *const u8,
466 account_buf_len: usize,
467 sequence: i32,
468 out_buf_ptr: *mut u8,
469 out_buf_cap: usize,
470) -> i32 {
471 if HASH256_LEN > out_buf_cap {
472 return HostError::BufferTooSmall as i32;
473 }
474 let mut data = get_data(account_buf_ptr, account_buf_len);
475 if ACCOUNT_ID_LEN != data.len() {
476 return HostError::InvalidAccount as i32;
477 }
478 let sqn_data = sequence.to_be_bytes();
479 data.extend_from_slice(&sqn_data);
480 let keylet_hash = index_hash(LedgerNameSpace::Check, &data);
481 set_data(keylet_hash.len() as i32, out_buf_ptr, keylet_hash);
482 HASH256_LEN as i32
483}
484
485#[allow(clippy::too_many_arguments)]
486pub fn credential_keylet(
487 _env: wasm_exec_env_t,
488 subject_ptr: *const u8,
489 subject_len: usize,
490 issuer_ptr: *const u8,
491 issuer_len: usize,
492 cred_type_ptr: *const u8,
493 cred_type_len: usize,
494 out_buf_ptr: *mut u8,
495 out_buf_cap: usize,
496) -> i32 {
497 if HASH256_LEN > out_buf_cap {
498 return HostError::BufferTooSmall as i32;
499 }
500 let subject = get_data(subject_ptr, subject_len); let mut issuer = get_data(issuer_ptr, issuer_len);
502 if ACCOUNT_ID_LEN != issuer.len() {
503 return HostError::InvalidAccount as i32;
504 }
505 let mut cred_type = get_data(cred_type_ptr, cred_type_len); let mut data = subject;
507 data.append(&mut issuer);
508 data.append(&mut cred_type);
509 let keylet_hash = index_hash(LedgerNameSpace::Credential, &data);
510 set_data(keylet_hash.len() as i32, out_buf_ptr, keylet_hash);
511 HASH256_LEN as i32
512}
513
514pub fn delegate_keylet(
515 _env: wasm_exec_env_t,
516 account_ptr: *const u8,
517 account_len: usize,
518 authorize_ptr: *const u8,
519 authorize_len: usize,
520 out_buf_ptr: *mut u8,
521 out_buf_cap: usize,
522) -> i32 {
523 if HASH256_LEN > out_buf_cap {
524 return HostError::BufferTooSmall as i32;
525 }
526 let mut data = get_data(account_ptr, account_len);
527 let mut authorized = get_data(authorize_ptr, authorize_len);
528 if ACCOUNT_ID_LEN != data.len() || ACCOUNT_ID_LEN != authorized.len() {
529 return HostError::InvalidAccount as i32;
530 }
531 data.append(&mut authorized);
532 let keylet_hash = index_hash(LedgerNameSpace::Delegate, &data);
533 set_data(keylet_hash.len() as i32, out_buf_ptr, keylet_hash);
534 HASH256_LEN as i32
535}
536
537pub fn deposit_preauth_keylet(
538 _env: wasm_exec_env_t,
539 account_ptr: *const u8,
540 account_len: usize,
541 authorize_ptr: *const u8,
542 authorize_len: usize,
543 out_buf_ptr: *mut u8,
544 out_buf_cap: usize,
545) -> i32 {
546 if HASH256_LEN > out_buf_cap {
547 return HostError::BufferTooSmall as i32;
548 }
549 let mut data = get_data(account_ptr, account_len);
550 let mut authorized = get_data(authorize_ptr, authorize_len);
551 if ACCOUNT_ID_LEN != data.len() || ACCOUNT_ID_LEN != authorized.len() {
552 return HostError::InvalidAccount as i32;
553 }
554 data.append(&mut authorized);
555 let keylet_hash = index_hash(LedgerNameSpace::DepositPreauth, &data);
556 set_data(keylet_hash.len() as i32, out_buf_ptr, keylet_hash);
557 HASH256_LEN as i32
558}
559
560pub fn did_keylet(
561 _env: wasm_exec_env_t,
562 account_ptr: *const u8,
563 account_len: usize,
564 out_buf_ptr: *mut u8,
565 out_buf_cap: usize,
566) -> i32 {
567 if HASH256_LEN > out_buf_cap {
568 return HostError::BufferTooSmall as i32;
569 }
570 let mut data = get_data(account_ptr, account_len);
571 if ACCOUNT_ID_LEN != data.len() {
572 return HostError::InvalidAccount as i32;
573 }
574 let keylet_hash = index_hash(LedgerNameSpace::Did, &data);
575 set_data(keylet_hash.len() as i32, out_buf_ptr, keylet_hash);
576 HASH256_LEN as i32
577}
578
579pub fn escrow_keylet(
580 _env: wasm_exec_env_t,
581 account_ptr: *const u8,
582 account_len: usize,
583 sequence: u32,
584 out_buf_ptr: *mut u8,
585 out_buf_cap: usize,
586) -> i32 {
587 if HASH256_LEN > out_buf_cap {
588 return HostError::BufferTooSmall as i32;
589 }
590 let mut data = get_data(account_ptr, account_len);
591 if ACCOUNT_ID_LEN != data.len() {
592 return HostError::InvalidAccount as i32;
593 }
594 let sqn_data = sequence.to_be_bytes();
595 data.extend_from_slice(&sqn_data);
596 let keylet_hash = index_hash(LedgerNameSpace::Escrow, &data);
597 set_data(keylet_hash.len() as i32, out_buf_ptr, keylet_hash);
598 HASH256_LEN as i32
599}
600
601#[allow(clippy::too_many_arguments)]
602pub fn line_keylet(
603 _env: wasm_exec_env_t,
604 account1_ptr: *const u8,
605 account1_len: usize,
606 account2_ptr: *const u8,
607 account2_len: usize,
608 currency_ptr: *const u8,
609 currency_len: usize,
610 out_buf_ptr: *mut u8,
611 out_buf_cap: usize,
612) -> i32 {
613 if HASH256_LEN > out_buf_cap {
614 return HostError::BufferTooSmall as i32;
615 }
616 let mut account1 = get_data(account1_ptr, account1_len);
617 let mut account2 = get_data(account2_ptr, account2_len);
618 let mut currency = get_data(currency_ptr, currency_len);
619 if ACCOUNT_ID_LEN != account1.len() || ACCOUNT_ID_LEN != account2.len() {
620 return HostError::InvalidAccount as i32;
621 }
622 if CURRENCY_LEN != currency.len() {
623 return HostError::InvalidParams as i32;
624 }
625 let mut data = account1;
626 data.append(&mut account2);
627 data.append(&mut currency);
628 let keylet_hash = index_hash(LedgerNameSpace::TrustLine, &data);
629 set_data(keylet_hash.len() as i32, out_buf_ptr, keylet_hash);
630 HASH256_LEN as i32
631}
632
633pub fn mpt_issuance_keylet(
634 _env: wasm_exec_env_t,
635 issuer_buf_ptr: *const u8,
636 issuer_buf_len: usize,
637 sequence: i32,
638 out_buf_ptr: *mut u8,
639 out_buf_cap: usize,
640) -> i32 {
641 if HASH256_LEN > out_buf_cap {
642 return HostError::BufferTooSmall as i32;
643 }
644 let mut account = get_data(issuer_buf_ptr, issuer_buf_len);
645 if ACCOUNT_ID_LEN != account.len() {
646 return HostError::InvalidAccount as i32;
647 }
648 let sqn_data = (sequence as u32).to_be_bytes();
650 let mut mpt_id: Vec<u8> = sqn_data.to_vec();
651 mpt_id.append(&mut account);
652 let data = mpt_id;
653 let keylet_hash = index_hash(LedgerNameSpace::MptokenIssuance, &data);
654 set_data(keylet_hash.len() as i32, out_buf_ptr, keylet_hash);
655 HASH256_LEN as i32
656}
657
658pub fn mptoken_keylet(
659 _env: wasm_exec_env_t,
660 mpt_id_ptr: *const u8,
661 mpt_id_len: usize,
662 holder_ptr: *const u8,
663 holder_len: usize,
664 out_buf_ptr: *mut u8,
665 out_buf_cap: usize,
666) -> i32 {
667 if HASH256_LEN > out_buf_cap {
668 return HostError::BufferTooSmall as i32;
669 }
670 let mut mpt_id = get_data(mpt_id_ptr, mpt_id_len);
671 let mut holder = get_data(holder_ptr, holder_len);
672 if MPT_ID_LEN != mpt_id.len() {
673 return HostError::InvalidParams as i32;
674 }
675 if ACCOUNT_ID_LEN != holder.len() {
676 return HostError::InvalidAccount as i32;
677 }
678 let mpt_id_hash = index_hash(LedgerNameSpace::MptokenIssuance, &mpt_id);
679 let mut data = mpt_id_hash;
680 data.append(&mut holder);
681 let keylet_hash = index_hash(LedgerNameSpace::Mptoken, &data);
682 set_data(keylet_hash.len() as i32, out_buf_ptr, keylet_hash);
683 HASH256_LEN as i32
684}
685
686pub fn nft_offer_keylet(
687 _env: wasm_exec_env_t,
688 account_buf_ptr: *const u8,
689 account_buf_len: usize,
690 sequence: i32,
691 out_buf_ptr: *mut u8,
692 out_buf_cap: usize,
693) -> i32 {
694 if HASH256_LEN > out_buf_cap {
695 return HostError::BufferTooSmall as i32;
696 }
697 let mut data = get_data(account_buf_ptr, account_buf_len);
698 if ACCOUNT_ID_LEN != data.len() {
699 return HostError::InvalidAccount as i32;
700 }
701 let sqn_data = sequence.to_be_bytes();
702 data.extend_from_slice(&sqn_data);
703 let keylet_hash = index_hash(LedgerNameSpace::NftokenOffer, &data);
704 set_data(keylet_hash.len() as i32, out_buf_ptr, keylet_hash);
705 HASH256_LEN as i32
706}
707
708pub fn offer_keylet(
709 _env: wasm_exec_env_t,
710 account_buf_ptr: *const u8,
711 account_buf_len: usize,
712 sequence: i32,
713 out_buf_ptr: *mut u8,
714 out_buf_cap: usize,
715) -> i32 {
716 if HASH256_LEN > out_buf_cap {
717 return HostError::BufferTooSmall as i32;
718 }
719 let mut data = get_data(account_buf_ptr, account_buf_len);
720 if ACCOUNT_ID_LEN != data.len() {
721 return HostError::InvalidAccount as i32;
722 }
723 let sqn_data = sequence.to_be_bytes();
724 data.extend_from_slice(&sqn_data);
725 let keylet_hash = index_hash(LedgerNameSpace::Offer, &data);
726 set_data(keylet_hash.len() as i32, out_buf_ptr, keylet_hash);
727 HASH256_LEN as i32
728}
729
730pub fn oracle_keylet(
731 _env: wasm_exec_env_t,
732 account_ptr: *const u8,
733 account_len: usize,
734 document_id: u32,
735 out_buf_ptr: *mut u8,
736 out_buf_cap: usize,
737) -> i32 {
738 if HASH256_LEN > out_buf_cap {
739 return HostError::BufferTooSmall as i32;
740 }
741 let mut data = get_data(account_ptr, account_len);
742 if ACCOUNT_ID_LEN != data.len() {
743 return HostError::InvalidAccount as i32;
744 }
745 let sqn_data = document_id.to_be_bytes();
746 data.extend_from_slice(&sqn_data);
747 let keylet_hash = index_hash(LedgerNameSpace::Oracle, &data);
748 set_data(keylet_hash.len() as i32, out_buf_ptr, keylet_hash);
749 HASH256_LEN as i32
750}
751
752#[allow(clippy::too_many_arguments)]
753pub fn paychan_keylet(
754 _env: wasm_exec_env_t,
755 account_ptr: *const u8,
756 account_len: usize,
757 destination_ptr: *const u8,
758 destination_len: usize,
759 sequence: i32,
760 out_buf_ptr: *mut u8,
761 out_buf_cap: usize,
762) -> i32 {
763 if HASH256_LEN > out_buf_cap {
764 return HostError::BufferTooSmall as i32;
765 }
766 let mut data = get_data(account_ptr, account_len);
767 let mut destination = get_data(destination_ptr, destination_len);
768 if ACCOUNT_ID_LEN != data.len() || ACCOUNT_ID_LEN != destination.len() {
769 return HostError::InvalidAccount as i32;
770 }
771 let sqn_data = sequence.to_be_bytes();
772 data.append(&mut destination);
773 data.extend_from_slice(&sqn_data);
774 let keylet_hash = index_hash(LedgerNameSpace::XrpPaymentChannel, &data);
775 set_data(keylet_hash.len() as i32, out_buf_ptr, keylet_hash);
776 HASH256_LEN as i32
777}
778
779pub fn permissioned_domain_keylet(
780 _env: wasm_exec_env_t,
781 account_buf_ptr: *const u8,
782 account_buf_len: usize,
783 sequence: i32,
784 out_buf_ptr: *mut u8,
785 out_buf_cap: usize,
786) -> i32 {
787 if HASH256_LEN > out_buf_cap {
788 return HostError::BufferTooSmall as i32;
789 }
790 let mut data = get_data(account_buf_ptr, account_buf_len);
791 if ACCOUNT_ID_LEN != data.len() {
792 return HostError::InvalidAccount as i32;
793 }
794 let sqn_data = sequence.to_be_bytes();
795 data.extend_from_slice(&sqn_data);
796 let keylet_hash = index_hash(LedgerNameSpace::PermissionedDomain, &data);
797 set_data(keylet_hash.len() as i32, out_buf_ptr, keylet_hash);
798 HASH256_LEN as i32
799}
800
801pub fn signers_keylet(
802 _env: wasm_exec_env_t,
803 account_buf_ptr: *const u8,
804 account_buf_len: usize,
805 out_buf_ptr: *mut u8,
806 out_buf_cap: usize,
807) -> i32 {
808 if HASH256_LEN > out_buf_cap {
809 return HostError::BufferTooSmall as i32;
810 }
811 let mut data = get_data(account_buf_ptr, account_buf_len);
812 if ACCOUNT_ID_LEN != data.len() {
813 return HostError::InvalidAccount as i32;
814 }
815 let default_signer_list_id = 0u32;
816 let sid_data = default_signer_list_id.to_be_bytes();
817 data.extend_from_slice(&sid_data);
818 let keylet_hash = index_hash(LedgerNameSpace::SignerList, &data);
819 set_data(keylet_hash.len() as i32, out_buf_ptr, keylet_hash);
820 HASH256_LEN as i32
821}
822
823pub fn ticket_keylet(
824 _env: wasm_exec_env_t,
825 account_buf_ptr: *const u8,
826 account_buf_len: usize,
827 sequence: i32,
828 out_buf_ptr: *mut u8,
829 out_buf_cap: usize,
830) -> i32 {
831 if HASH256_LEN > out_buf_cap {
832 return HostError::BufferTooSmall as i32;
833 }
834 let mut data = get_data(account_buf_ptr, account_buf_len);
835 if ACCOUNT_ID_LEN != data.len() {
836 return HostError::InvalidAccount as i32;
837 }
838 let sqn_data = sequence.to_be_bytes();
839 data.extend_from_slice(&sqn_data);
840 let keylet_hash = index_hash(LedgerNameSpace::Ticket, &data);
841 set_data(keylet_hash.len() as i32, out_buf_ptr, keylet_hash);
842 HASH256_LEN as i32
843}
844
845pub fn vault_keylet(
846 _env: wasm_exec_env_t,
847 account_buf_ptr: *const u8,
848 account_buf_len: usize,
849 sequence: i32,
850 out_buf_ptr: *mut u8,
851 out_buf_cap: usize,
852) -> i32 {
853 if HASH256_LEN > out_buf_cap {
854 return HostError::BufferTooSmall as i32;
855 }
856 let mut data = get_data(account_buf_ptr, account_buf_len);
857 if ACCOUNT_ID_LEN != data.len() {
858 return HostError::InvalidAccount as i32;
859 }
860 let sqn_data = sequence.to_be_bytes();
861 data.extend_from_slice(&sqn_data);
862 let keylet_hash = index_hash(LedgerNameSpace::Vault, &data);
863 set_data(keylet_hash.len() as i32, out_buf_ptr, keylet_hash);
864 HASH256_LEN as i32
865}
866
867pub fn get_nft(
868 env: wasm_exec_env_t,
869 owner_ptr: *const u8,
870 owner_len: usize,
871 nft_id_ptr: *const u8,
872 nft_id_len: usize,
873 out_buf_ptr: *mut u8,
874 out_buf_cap: usize,
875) -> i32 {
876 let data_provider = get_dp(env);
877 let owner_id = get_data(owner_ptr, owner_len);
878 if ACCOUNT_ID_LEN != owner_id.len() {
879 return HostError::InvalidAccount as i32;
880 }
881 let nft_id = get_data(nft_id_ptr, nft_id_len);
882 if HASH256_LEN != nft_id.len() {
883 return HostError::InvalidParams as i32;
884 }
885 let dp_res = data_provider.get_nft_uri(&nft_id, &owner_id, out_buf_cap);
886 set_data(dp_res.0, out_buf_ptr, dp_res.1);
887 dp_res.0
888}
889
890fn unpack_in_float(env: wasm_exec_env_t, in_buf: *const u8) -> Result<Number, HostError> {
891 let bytes: [u8; 8] = unsafe {
892 let inst = wasm_runtime_get_module_inst(env);
893 if !wasm_runtime_validate_native_addr(inst, in_buf as *mut ::core::ffi::c_void, 8) {
894 return Err(HostError::PointerOutOfBound);
895 }
896 match std::slice::from_raw_parts(in_buf, 8).try_into() {
897 Ok(bytes) => bytes,
898 Err(_) => return Err(HostError::InvalidFloatInput),
899 }
900 };
901
902 Number::from_xrpl_iou_value(bytes).map_err(|_| HostError::InvalidFloatInput)
903}
904
905fn pack_out_float(number: Number, env: wasm_exec_env_t, out_buf: *mut u8) -> i32 {
906 let bytes = match number.to_xrpl_iou_value() {
908 Ok(bytes) => bytes,
909 Err(_) => return HostError::InvalidFloatComputation as i32,
910 };
911
912 unsafe {
913 let inst = wasm_runtime_get_module_inst(env);
914 if !wasm_runtime_validate_native_addr(inst, out_buf as *mut ::core::ffi::c_void, 8) {
915 return HostError::PointerOutOfBound as i32;
916 }
917 std::ptr::copy_nonoverlapping(bytes.as_ptr(), out_buf, 8);
918 }
919
920 8
921}
922
923#[allow(clippy::too_many_arguments)]
924pub fn float_add(
925 env: wasm_exec_env_t,
926 in_buff1: *const u8,
927 in_buff1_len: usize,
928 in_buff2: *const u8,
929 in_buff2_len: usize,
930 out_buff: *mut u8,
931 out_buff_len: usize,
932 rounding_mode: i32,
933) -> i32 {
934 let _rounding_guard = set_rounding_mode_from_param(rounding_mode);
935
936 let n1 = match unpack_in_float(env, in_buff1) {
937 Ok(val) => val,
938 Err(e) => return e as i32,
939 };
940 let n2 = match unpack_in_float(env, in_buff2) {
941 Ok(val) => val,
942 Err(e) => return e as i32,
943 };
944 let result = match (&n1 + &n2) {
945 Ok(r) => r,
946 Err(_) => return HostError::InvalidFloatComputation as i32,
947 };
948
949 pack_out_float(result, env, out_buff)
950}
951
952pub fn float_from_int(
953 env: wasm_exec_env_t,
954 in_int: i64,
955 out_buf: *mut u8,
956 out_buff_len: usize,
957 rounding_mode: i32,
958) -> i32 {
959 let _rounding_guard = set_rounding_mode_from_param(rounding_mode);
960
961 let number = match Number::from_i64(in_int) {
962 Ok(n) => n,
963 Err(_) => return HostError::InvalidFloatComputation as i32,
964 };
965
966 pack_out_float(number, env, out_buf)
967}
968
969pub fn float_from_uint(
970 env: wasm_exec_env_t,
971 in_uint_ptr: *const u8,
972 in_uint_len: usize,
973 out_buff: *mut u8,
974 out_buff_len: usize,
975 rounding_mode: i32,
976) -> i32 {
977 let _rounding_guard = set_rounding_mode_from_param(rounding_mode);
978
979 let v: u64 = unsafe {
980 let inst = wasm_runtime_get_module_inst(env);
981 if !wasm_runtime_validate_native_addr(inst, in_uint_ptr as *mut ::core::ffi::c_void, 8) {
982 return HostError::PointerOutOfBound as i32;
983 }
984 let bytes: [u8; 8] = match std::slice::from_raw_parts(in_uint_ptr, 8).try_into() {
985 Ok(bytes) => bytes,
986 Err(_) => return HostError::InvalidFloatInput as i32,
987 };
988 u64::from_le_bytes(bytes)
989 };
990
991 let signed_val = if v <= i64::MAX as u64 {
993 v as i64
994 } else {
995 return HostError::InvalidFloatComputation as i32;
996 };
997
998 let number = match Number::from_i64(signed_val) {
999 Ok(n) => n,
1000 Err(_) => return HostError::InvalidFloatComputation as i32,
1001 };
1002
1003 pack_out_float(number, env, out_buff)
1004}
1005
1006pub fn float_set(
1007 env: wasm_exec_env_t,
1008 exponent: i32,
1009 mantissa: i64,
1010 out_buff: *mut u8,
1011 out_buff_len: usize,
1012 rounding_mode: i32,
1013) -> i32 {
1014 let _rounding_guard = set_rounding_mode_from_param(rounding_mode);
1015
1016 let number = match Number::from_mantissa_exponent(mantissa, exponent) {
1017 Ok(n) => n,
1018 Err(_) => return HostError::InvalidFloatComputation as i32,
1019 };
1020
1021 pack_out_float(number, env, out_buff)
1022}
1023
1024pub fn float_compare(
1025 env: wasm_exec_env_t,
1026 in_buff1: *const u8,
1027 in_buff1_len: usize,
1028 in_buff2: *const u8,
1029 in_buff2_len: usize,
1030) -> i32 {
1031 let n1 = match unpack_in_float(env, in_buff1) {
1032 Ok(val) => val,
1033 Err(e) => return e as i32,
1034 };
1035 let n2 = match unpack_in_float(env, in_buff2) {
1036 Ok(val) => val,
1037 Err(e) => return e as i32,
1038 };
1039
1040 match n1.cmp(&n2) {
1041 std::cmp::Ordering::Equal => 0,
1042 std::cmp::Ordering::Greater => 1,
1043 std::cmp::Ordering::Less => 2,
1044 }
1045}
1046
1047#[allow(clippy::too_many_arguments)]
1048pub fn float_subtract(
1049 env: wasm_exec_env_t,
1050 in_buff1: *const u8,
1051 in_buff1_len: usize,
1052 in_buff2: *const u8,
1053 in_buff2_len: usize,
1054 out_buff: *mut u8,
1055 out_buff_len: usize,
1056 rounding_mode: i32,
1057) -> i32 {
1058 let _rounding_guard = set_rounding_mode_from_param(rounding_mode);
1059
1060 let n1 = match unpack_in_float(env, in_buff1) {
1061 Ok(val) => val,
1062 Err(e) => return e as i32,
1063 };
1064 let n2 = match unpack_in_float(env, in_buff2) {
1065 Ok(val) => val,
1066 Err(e) => return e as i32,
1067 };
1068
1069 let result = match (&n1 - &n2) {
1070 Ok(r) => r,
1071 Err(_) => return HostError::InvalidFloatComputation as i32,
1072 };
1073
1074 pack_out_float(result, env, out_buff)
1075}
1076
1077#[allow(clippy::too_many_arguments)]
1078pub fn float_multiply(
1079 env: wasm_exec_env_t,
1080 in_buff1: *const u8,
1081 in_buff1_len: usize,
1082 in_buff2: *const u8,
1083 in_buff2_len: usize,
1084 out_buff: *mut u8,
1085 out_buff_len: usize,
1086 rounding_mode: i32,
1087) -> i32 {
1088 let _rounding_guard = set_rounding_mode_from_param(rounding_mode);
1089
1090 let n1 = match unpack_in_float(env, in_buff1) {
1091 Ok(val) => val,
1092 Err(e) => return e as i32,
1093 };
1094 let n2 = match unpack_in_float(env, in_buff2) {
1095 Ok(val) => val,
1096 Err(e) => return e as i32,
1097 };
1098
1099 let result = match (&n1 * &n2) {
1100 Ok(r) => r,
1101 Err(_) => return HostError::InvalidFloatComputation as i32,
1102 };
1103
1104 pack_out_float(result, env, out_buff)
1105}
1106
1107#[allow(clippy::too_many_arguments)]
1108pub fn float_divide(
1109 env: wasm_exec_env_t,
1110 in_buff1: *const u8,
1111 in_buff1_len: usize,
1112 in_buff2: *const u8,
1113 in_buff2_len: usize,
1114 out_buff: *mut u8,
1115 out_buff_len: usize,
1116 rounding_mode: i32,
1117) -> i32 {
1118 let _rounding_guard = set_rounding_mode_from_param(rounding_mode);
1119
1120 let n1 = match unpack_in_float(env, in_buff1) {
1121 Ok(val) => val,
1122 Err(e) => return e as i32,
1123 };
1124 let n2 = match unpack_in_float(env, in_buff2) {
1125 Ok(val) => val,
1126 Err(e) => return e as i32,
1127 };
1128
1129 let result = match (&n1 / &n2) {
1130 Ok(r) => r,
1131 Err(_) => return HostError::InvalidFloatComputation as i32,
1132 };
1133
1134 pack_out_float(result, env, out_buff)
1135}
1136
1137pub fn float_pow(
1138 env: wasm_exec_env_t,
1139 in_buff: *const u8,
1140 in_buff_len: usize,
1141 in_int: i32,
1142 out_buff: *mut u8,
1143 out_buff_len: usize,
1144 rounding_mode: i32,
1145) -> i32 {
1146 let _rounding_guard = set_rounding_mode_from_param(rounding_mode);
1147
1148 let n = match unpack_in_float(env, in_buff) {
1149 Ok(val) => val,
1150 Err(e) => return e as i32,
1151 };
1152
1153 if in_int < 0 {
1154 return HostError::InvalidParams as i32;
1155 }
1156
1157 if n.is_zero() && in_int == 0 {
1159 return HostError::InvalidParams as i32;
1160 }
1161
1162 let result = match n.pow(in_int as u32) {
1163 Ok(r) => r,
1164 Err(_) => return HostError::InvalidFloatComputation as i32,
1165 };
1166
1167 pack_out_float(result, env, out_buff)
1168}
1169
1170pub fn float_root(
1171 env: wasm_exec_env_t,
1172 in_buff: *const u8,
1173 in_buff_len: usize,
1174 in_int: i32,
1175 out_buff: *mut u8,
1176 out_buff_len: usize,
1177 rounding_mode: i32,
1178) -> i32 {
1179 let _rounding_guard = set_rounding_mode_from_param(rounding_mode);
1180
1181 let n = match unpack_in_float(env, in_buff) {
1182 Ok(val) => val,
1183 Err(e) => return e as i32,
1184 };
1185
1186 if in_int <= 0 {
1187 return HostError::InvalidParams as i32;
1188 }
1189
1190 let result = match n.root(in_int as u32) {
1191 Ok(r) => r,
1192 Err(_) => return HostError::InvalidFloatComputation as i32,
1193 };
1194
1195 pack_out_float(result, env, out_buff)
1196}
1197
1198pub fn float_log(
1199 env: wasm_exec_env_t,
1200 in_buff: *const u8,
1201 in_buff_len: usize,
1202 out_buff: *mut u8,
1203 out_buff_len: usize,
1204 rounding_mode: i32,
1205) -> i32 {
1206 let _rounding_guard = set_rounding_mode_from_param(rounding_mode);
1207
1208 let n = match unpack_in_float(env, in_buff) {
1209 Ok(val) => val,
1210 Err(e) => return e as i32,
1211 };
1212
1213 let result = match n.log10() {
1214 Ok(r) => r,
1215 Err(_) => return HostError::InvalidFloatComputation as i32,
1216 };
1217
1218 pack_out_float(result, env, out_buff)
1219}
1220
1221fn read_utf8_from_wasm(msg_read_ptr: *const u8, msg_read_len: usize) -> Option<String> {
1224 String::from_utf8(get_data(msg_read_ptr, msg_read_len)).ok()
1225}
1226fn read_hex_from_wasm(
1227 data_read_ptr: *const u8,
1228 data_read_len: usize,
1229 data_as_hex: bool,
1230) -> Option<String> {
1231 if data_as_hex {
1232 let bytes_vec: Vec<u8> = get_data(data_read_ptr, data_read_len);
1234 let mut final_hex_string = "0x".to_owned();
1235 let hex_data = hex::encode_upper(&bytes_vec);
1236 final_hex_string.push_str(hex_data.as_str());
1237 Some(final_hex_string)
1238 } else {
1239 read_utf8_from_wasm(data_read_ptr, data_read_len)
1240 }
1241}
1242
1243pub fn trace(
1244 _env: wasm_exec_env_t,
1245 msg_read_ptr: *const u8,
1246 msg_read_len: usize,
1247 data_read_ptr: *const u8,
1248 data_read_len: usize,
1249 data_as_hex: i32,
1250) -> i32 {
1251 if msg_read_len > MAX_WASM_PARAM_LENGTH || data_read_len > MAX_WASM_PARAM_LENGTH {
1255 return HostError::DataFieldTooLarge as i32;
1256 }
1257
1258 let data_as_hex = {
1259 match data_as_hex {
1260 0 => false,
1261 1 => true,
1262 _ => true,
1264 }
1265 };
1266
1267 debug!(
1268 "trace() params: msg_read_ptr={:?} msg_read_len={} data_read_ptr={:?} data_read_len={}",
1269 msg_read_ptr, msg_read_len, data_read_ptr, data_read_len
1270 );
1271
1272 let Some(message) = read_utf8_from_wasm(msg_read_ptr, msg_read_len) else {
1273 return HostError::InvalidDecoding as i32;
1274 };
1275
1276 let Some(data_string) = read_hex_from_wasm(data_read_ptr, data_read_len, data_as_hex) else {
1277 return HostError::InvalidDecoding as i32;
1278 };
1279
1280 if data_read_len > 0 {
1281 println!(
1282 "WASM TRACE: {message} ({data_string} | {} data bytes)",
1283 data_read_len
1284 );
1285 } else {
1286 println!("WASM TRACE: {message}");
1287 }
1288
1289 (data_read_len + msg_read_len + 1) as i32
1290}
1291
1292pub fn trace_num(
1293 _env: wasm_exec_env_t,
1294 msg_read_ptr: *const u8,
1295 msg_read_len: usize,
1296 number: i64,
1297) -> i32 {
1298 if msg_read_len > MAX_WASM_PARAM_LENGTH {
1302 return HostError::DataFieldTooLarge as i32;
1303 }
1304
1305 debug!(
1306 "trace() params: msg_read_ptr={:?} msg_read_len={} number={} ",
1307 msg_read_ptr, msg_read_len, number
1308 );
1309 let Some(message) = read_utf8_from_wasm(msg_read_ptr, msg_read_len) else {
1310 return HostError::InvalidDecoding as i32;
1311 };
1312
1313 if (number < 0) {
1314 let error_code_str = error_code_to_string(number);
1315 println!("WASM TRACE[ERROR]: {message} {error_code_str}");
1316 } else {
1317 println!("WASM TRACE: {message} {number}");
1318 }
1319 0
1320}
1321
1322pub fn trace_opaque_float(
1323 _env: wasm_exec_env_t,
1324 msg_read_ptr: *const u8,
1325 msg_read_len: usize,
1326 op_float: *const u8,
1327 float_len: usize,
1328) -> i32 {
1329 if msg_read_len > MAX_WASM_PARAM_LENGTH || float_len > MAX_WASM_PARAM_LENGTH {
1330 return HostError::DataFieldTooLarge as i32;
1331 }
1332 let bytes: [u8; 8] = unsafe {
1333 match std::slice::from_raw_parts(op_float, 8).try_into() {
1334 Ok(bytes) => bytes,
1335 Err(_) => return HostError::InvalidFloatInput as i32,
1336 }
1337 };
1338
1339 let f = match _deserialize_issued_currency_amount(bytes) {
1340 Ok(f) => f,
1341 Err(_) => return HostError::InvalidFloatInput as i32,
1342 };
1343
1344 debug!(
1345 "trace() params: msg_read_ptr={:?} msg_read_len={} float={} ",
1346 msg_read_ptr, msg_read_len, f
1347 );
1348 let Some(message) = read_utf8_from_wasm(msg_read_ptr, msg_read_len) else {
1349 return HostError::InvalidDecoding as i32;
1350 };
1351
1352 println!("WASM TRACE: {message} {f}");
1353 0
1354}
1355
1356pub fn trace_account(
1357 _env: wasm_exec_env_t,
1358 msg_read_ptr: *const u8,
1359 msg_read_len: usize,
1360 account_ptr: *const u8,
1361 account_len: usize,
1362) -> i32 {
1363 if msg_read_len > MAX_WASM_PARAM_LENGTH || account_len > MAX_WASM_PARAM_LENGTH {
1367 return HostError::DataFieldTooLarge as i32;
1368 }
1369 if ACCOUNT_ID_LEN != account_len {
1370 return HostError::InvalidAccount as i32;
1371 }
1372
1373 debug!(
1374 "trace() params: msg_read_ptr={:?} msg_read_len={} account_ptr={:?} account_len={}",
1375 msg_read_ptr, msg_read_len, account_ptr, account_len
1376 );
1377
1378 let Some(message) = read_utf8_from_wasm(msg_read_ptr, msg_read_len) else {
1379 return HostError::InvalidDecoding as i32;
1380 };
1381
1382 let bytes: [u8; ACCOUNT_ID_LEN] = unsafe {
1383 match std::slice::from_raw_parts(account_ptr, account_len).try_into() {
1384 Ok(arr) => arr,
1385 Err(_) => return HostError::InvalidAccount as i32,
1386 }
1387 };
1388 let account_id = match encode_base58(&bytes, &[0x0], Some(20)) {
1389 Ok(val) => val,
1390 Err(_) => return HostError::InvalidAccount as i32,
1391 };
1392
1393 if account_len > 0 {
1394 println!(
1395 "WASM TRACE: {message} ({account_id} | {} data bytes)",
1396 account_len
1397 );
1398 } else {
1399 println!("WASM TRACE: {message}");
1400 }
1401
1402 (account_id.len() + msg_read_len + 1) as i32
1403}
1404
1405pub fn trace_amount(
1406 _env: wasm_exec_env_t,
1407 msg_read_ptr: *const u8,
1408 msg_read_len: usize,
1409 amount_ptr: *const u8,
1410 amount_len: usize,
1411) -> i32 {
1412 if msg_read_len > MAX_WASM_PARAM_LENGTH || amount_len > MAX_WASM_PARAM_LENGTH {
1416 return HostError::DataFieldTooLarge as i32;
1417 }
1418
1419 const TOKEN_AMOUNT_SIZE: usize = 48;
1421 if amount_len != TOKEN_AMOUNT_SIZE {
1422 return HostError::InvalidParams as i32;
1423 }
1424
1425 debug!(
1426 "trace_amount() params: msg_read_ptr={:?} msg_read_len={} amount_ptr={:?} amount_len={}",
1427 msg_read_ptr, msg_read_len, amount_ptr, amount_len
1428 );
1429
1430 let Some(message) = read_utf8_from_wasm(msg_read_ptr, msg_read_len) else {
1431 return HostError::InvalidDecoding as i32;
1432 };
1433
1434 let amount_bytes: [u8; TOKEN_AMOUNT_SIZE] = unsafe {
1435 match std::slice::from_raw_parts(amount_ptr, amount_len).try_into() {
1436 Ok(arr) => arr,
1437 Err(_) => return HostError::InvalidParams as i32,
1438 }
1439 };
1440
1441 let amount_info = parse_stamount_for_display(&amount_bytes);
1443
1444 println!(
1445 "WASM TRACE: {message} ({amount_info} | {} amount bytes)",
1446 amount_len
1447 );
1448
1449 (amount_info.len() + msg_read_len + 1) as i32
1450}
1451
1452fn parse_stamount_for_display(bytes: &[u8; 48]) -> String {
1455 match TokenAmount::from_bytes(bytes) {
1457 Ok(token_amount) => format_token_amount_for_display(&token_amount),
1458 Err(_) => {
1459 format!(
1461 "Unknown amount format: 0x{}",
1462 hex::encode_upper(&bytes[0..8])
1463 )
1464 }
1465 }
1466}
1467
1468fn format_token_amount_for_display(token_amount: &TokenAmount) -> String {
1470 match token_amount {
1471 TokenAmount::XRP { num_drops } => {
1472 format!("XRP: {} drops", num_drops.abs())
1473 }
1474 TokenAmount::MPT {
1475 num_units,
1476 is_positive,
1477 mpt_id,
1478 } => {
1479 let sign_str = if *is_positive { "+" } else { "-" };
1480 let sequence = mpt_id.get_sequence_num();
1481 let issuer_bytes = mpt_id.get_issuer().0;
1482 let issuer = match encode_base58(&issuer_bytes, &[0x0], Some(20)) {
1483 Ok(addr) => addr,
1484 Err(_) => hex::encode_upper(issuer_bytes),
1485 };
1486 format!(
1487 "MPT: {}{} units, Sequence: {}, Issuer: {}",
1488 sign_str, num_units, sequence, issuer
1489 )
1490 }
1491 TokenAmount::IOU {
1492 amount,
1493 issuer,
1494 currency_code,
1495 } => {
1496 let amount_str = match _deserialize_issued_currency_amount(amount.0) {
1498 Ok(value) => format!("{}", value),
1499 Err(_) => format!("0x{}", hex::encode_upper(amount.0)),
1500 };
1501
1502 let currency_str = format_currency_code(currency_code.as_bytes());
1503 let issuer_str = match encode_base58(&issuer.0, &[0x0], Some(20)) {
1504 Ok(addr) => addr,
1505 Err(_) => hex::encode_upper(issuer.0),
1506 };
1507
1508 format!(
1509 "IOU: {} {}, Issuer: {}",
1510 amount_str, currency_str, issuer_str
1511 )
1512 }
1513 }
1514}
1515
1516fn format_currency_code(currency_bytes: &[u8; 20]) -> String {
1518 if currency_bytes[0..12].iter().all(|&b| b == 0)
1520 && currency_bytes[15..20].iter().all(|&b| b == 0)
1521 && currency_bytes[12..15].iter().any(|&b| b != 0)
1522 {
1523 let code_bytes = ¤cy_bytes[12..15];
1525 if let Ok(code_str) = std::str::from_utf8(code_bytes)
1526 && code_str.chars().all(|c| c.is_ascii_alphanumeric())
1527 {
1528 return code_str.to_string();
1529 }
1530 }
1531
1532 format!("0x{}", hex::encode_upper(currency_bytes))
1534}
1535
1536#[cfg(test)]
1537mod tests {
1538 use super::*;
1539 use xrpl_wasm_std::core::types::{
1540 account_id::AccountID,
1541 amount::{
1542 currency_code::CurrencyCode, mpt_id::MptId, opaque_float::OpaqueFloat,
1543 token_amount::TokenAmount,
1544 },
1545 };
1546
1547 #[test]
1548 fn test_parse_stamount_for_display_xrp() {
1549 let xrp_amount = TokenAmount::XRP {
1551 num_drops: 1_000_000,
1552 };
1553 let (bytes, _) = xrp_amount.to_stamount_bytes();
1554
1555 let display_str = parse_stamount_for_display(&bytes);
1556 assert_eq!(display_str, "XRP: 1000000 drops");
1557 }
1558
1559 #[test]
1560 fn test_parse_stamount_for_display_mpt() {
1561 let issuer = AccountID::from([0xAB; 20]);
1563 let mpt_id = MptId::new(12345, issuer);
1564 let mpt_amount = TokenAmount::MPT {
1565 num_units: 750_000,
1566 is_positive: true,
1567 mpt_id,
1568 };
1569 let (bytes, _) = mpt_amount.to_stamount_bytes();
1570
1571 let display_str = parse_stamount_for_display(&bytes);
1572 assert!(display_str.starts_with("MPT: +750000 units"));
1573 assert!(display_str.contains("Sequence: 12345"));
1574 }
1575
1576 #[test]
1577 fn test_parse_stamount_for_display_iou() {
1578 let opaque_float = OpaqueFloat([0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]);
1580 let currency_code = CurrencyCode::from([0u8; 20]); let issuer = AccountID::from([0xCD; 20]);
1582
1583 let iou_amount = TokenAmount::IOU {
1584 amount: opaque_float,
1585 issuer,
1586 currency_code,
1587 };
1588 let (bytes, _) = iou_amount.to_stamount_bytes();
1589
1590 let display_str = parse_stamount_for_display(&bytes);
1591 assert!(display_str.starts_with("IOU:"));
1592 assert!(display_str.contains("Issuer:"));
1593 }
1594}