xrpl_wasm_stdlib/host/
trace.rs

1use crate::host::error_codes::match_result_code;
2
3use crate::core::types::account_id::AccountID;
4use crate::core::types::amount::Amount;
5use crate::host;
6use crate::host::Result;
7use core::ptr;
8
9/// Data representation
10#[derive(Clone, Copy)]
11pub enum DataRepr {
12    /// As UTF-8
13    AsUTF8 = 0,
14    /// As hexadecimal
15    AsHex = 1,
16}
17
18/// Write the contents of a message to the xrpld trace log.
19///
20/// # Parameters
21/// * `msg`: A str ref pointing to an array of bytes containing UTF-8 characters.
22///
23/// # Returns
24///
25/// Returns an integer representing the result of the operation. A value of `0` or higher signifies
26/// the number of message bytes that were written to the trace function. Non-zero values indicate
27/// an error (e.g., incorrect buffer sizes).
28#[inline(always)] // <-- Inline because this function is very small
29pub fn trace(msg: &str) -> Result<i32> {
30    let null_ptr: *const u8 = ptr::null::<u8>();
31
32    let result_code = unsafe {
33        host::trace(
34            msg.as_ptr(),
35            msg.len(),
36            null_ptr,
37            0usize,
38            DataRepr::AsUTF8 as _,
39        )
40    };
41
42    match_result_code(result_code, || result_code)
43}
44
45/// Write the contents of a message to the xrpld trace log.
46///
47/// # Parameters
48/// * `msg`: A str ref pointing to an array of bytes containing UTF-8 characters.
49///
50/// # Returns
51///
52/// Returns an integer representing the result of the operation. A value of `0` or higher signifies
53/// the number of message bytes that were written to the trace function. Non-zero values indicate
54/// an error (e.g., incorrect buffer sizes).
55#[inline(always)] // <-- Inline because this function is very small
56pub fn trace_data(msg: &str, data: &[u8], data_repr: DataRepr) -> Result<i32> {
57    let result_code = unsafe {
58        let data_ptr = data.as_ptr();
59        let data_len = data.len();
60        host::trace(msg.as_ptr(), msg.len(), data_ptr, data_len, data_repr as _)
61    };
62
63    match_result_code(result_code, || result_code)
64}
65
66/// Write the contents of a message, and a number, to the xrpld trace log.
67///
68/// # Parameters
69/// * `msg`: A str ref pointing to an array of bytes containing UTF-8 characters.
70/// * `number`: A number to emit into the trace logs.
71///
72/// # Returns
73///
74/// Returns an integer representing the result of the operation. A value of `0` or higher signifies
75/// the number of message bytes that were written to the trace function. Non-zero values indicate
76/// an error (e.g., incorrect buffer sizes).
77#[inline(always)]
78pub fn trace_num(msg: &str, number: i64) -> Result<i32> {
79    let result_code = unsafe { host::trace_num(msg.as_ptr(), msg.len(), number) };
80    match_result_code(result_code, || result_code)
81}
82
83#[inline(always)]
84pub fn trace_account_buf(msg: &str, account_id: &[u8; 20]) -> Result<i32> {
85    let result_code = unsafe {
86        host::trace_account(
87            msg.as_ptr(),
88            msg.len(),
89            account_id.as_ptr(),
90            account_id.len(),
91        )
92    };
93    match_result_code(result_code, || result_code)
94}
95
96#[inline(always)]
97pub fn trace_account(msg: &str, account_id: &AccountID) -> Result<i32> {
98    let result_code = unsafe {
99        host::trace_account(
100            msg.as_ptr(),
101            msg.len(),
102            account_id.0.as_ptr(),
103            account_id.0.len(),
104        )
105    };
106    match_result_code(result_code, || result_code)
107}
108
109#[inline(always)]
110pub fn trace_amount(msg: &str, amount: &Amount) -> Result<i32> {
111    // Convert Amount to the STAmount format expected by the host trace function
112    let (amount_bytes, len) = amount.to_stamount_bytes();
113
114    let result_code =
115        unsafe { host::trace_amount(msg.as_ptr(), msg.len(), amount_bytes.as_ptr(), len) };
116
117    match_result_code(result_code, || result_code)
118}
119
120/// Write a float to the XRPLD trace log
121#[inline(always)]
122pub fn trace_float(msg: &str, f: &[u8; 8]) -> Result<i32> {
123    let result_code = unsafe { host::trace_opaque_float(msg.as_ptr(), msg.len(), f.as_ptr(), 8) };
124    match_result_code(result_code, || result_code)
125}
126
127#[cfg(test)]
128mod tests {
129    use super::*;
130    use crate::core::types::amount::Amount;
131
132    #[test]
133    fn test_trace_amount_xrp() {
134        // Create a test XRP Amount
135        let amount = Amount::XRP {
136            num_drops: 1_000_000,
137        };
138        let message = "Test XRP amount";
139
140        // Call trace_amount function
141        let result = trace_amount(message, &amount);
142
143        // Should return Ok
144        assert!(result.is_ok());
145    }
146
147    #[test]
148    fn test_trace_amount_mpt() {
149        // Create a test MPT Amount
150        use crate::core::types::account_id::AccountID;
151        use crate::core::types::mpt_id::MptId;
152
153        const VALUE: u64 = 500_000;
154        const SEQUENCE_NUM: u32 = 12345;
155        const ISSUER_BYTES: [u8; 20] = [1u8; 20];
156
157        let issuer = AccountID::from(ISSUER_BYTES);
158        let mpt_id = MptId::new(SEQUENCE_NUM, issuer);
159        let amount = Amount::MPT {
160            num_units: VALUE,
161            is_positive: true,
162            mpt_id,
163        };
164
165        let message = "Test MPT amount";
166
167        // Call trace_amount function
168        let result = trace_amount(message, &amount);
169
170        // Should return Ok
171        assert!(result.is_ok());
172    }
173
174    #[test]
175    fn test_trace_amount_iou() {
176        // Create a test IOU Amount
177        use crate::core::types::account_id::AccountID;
178        use crate::core::types::currency::Currency;
179        use crate::core::types::opaque_float::OpaqueFloat;
180
181        let currency_bytes = [2u8; 20];
182        let issuer_bytes = [3u8; 20];
183        let amount_bytes = [0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x39]; // Simple test float
184
185        let currency = Currency::from(currency_bytes);
186        let issuer = AccountID::from(issuer_bytes);
187        let amount = OpaqueFloat(amount_bytes);
188
189        let amount = Amount::IOU {
190            amount,
191            issuer,
192            currency,
193        };
194
195        let message = "Test IOU amount";
196
197        // Call trace_amount function
198        let result = trace_amount(message, &amount);
199
200        // Should return Ok
201        assert!(result.is_ok());
202    }
203
204    #[test]
205    fn test_trace_amount_negative_xrp() {
206        // Create a test negative XRP Amount
207        let amount = Amount::XRP {
208            num_drops: -1_000_000,
209        };
210        let message = "Test negative XRP amount";
211
212        // Call trace_amount function
213        let result = trace_amount(message, &amount);
214
215        // Should return Ok
216        assert!(result.is_ok());
217    }
218
219    #[test]
220    fn test_trace_bytes_format() {
221        // Test XRP format
222        let xrp_amount = Amount::XRP {
223            num_drops: 1_000_000,
224        };
225        let (_bytes, len) = xrp_amount.to_stamount_bytes();
226        assert_eq!(len, 48); // All Amount types should return 48 bytes
227
228        // Test specific fee amount (10 drops)
229        let fee_amount = Amount::XRP { num_drops: 10 };
230        let (bytes, len) = fee_amount.to_stamount_bytes();
231        assert_eq!(len, 48); // All Amount types should return 48 bytes
232
233        // Check the actual bytes for 10 drops
234        // Expected: just the raw drop amount (10)
235        let expected_bytes = 10u64.to_be_bytes();
236        assert_eq!(&bytes[0..8], &expected_bytes);
237
238        // Test IOU format
239        use crate::core::types::account_id::AccountID;
240        use crate::core::types::currency::Currency;
241        use crate::core::types::opaque_float::OpaqueFloat;
242
243        let currency_bytes = [2u8; 20];
244        let issuer_bytes = [3u8; 20];
245        let amount_bytes = [0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x39];
246
247        let iou_amount = Amount::IOU {
248            amount: OpaqueFloat(amount_bytes),
249            issuer: AccountID::from(issuer_bytes),
250            currency: Currency::from(currency_bytes),
251        };
252        let (bytes, len) = iou_amount.to_stamount_bytes();
253        assert_eq!(len, 48); // All Amount types should return 48 bytes
254        assert_eq!(&bytes[0..8], &amount_bytes); // Should match the opaque float bytes
255
256        // Test MPT format
257        use crate::core::types::mpt_id::MptId;
258
259        const VALUE: u64 = 500_000;
260        const SEQUENCE_NUM: u32 = 12345;
261        const ISSUER_BYTES: [u8; 20] = [1u8; 20];
262
263        let issuer = AccountID::from(ISSUER_BYTES);
264        let mpt_id = MptId::new(SEQUENCE_NUM, issuer);
265        let mpt_amount = Amount::MPT {
266            num_units: VALUE,
267            is_positive: true,
268            mpt_id,
269        };
270        let (bytes, len) = mpt_amount.to_stamount_bytes();
271        assert_eq!(len, 48); // All Amount types should return 48 bytes
272        assert_eq!(bytes[0], 0b_0110_0000); // Positive MPT prefix
273        assert_eq!(&bytes[1..9], &VALUE.to_be_bytes()); // Amount bytes
274    }
275}