xrpl_common_stdlib/types/
issue.rs1use crate::fields::decoder::{FieldDecoder, FromLedger};
2use crate::host::Result;
3use crate::types::account_id::AccountID;
4use crate::types::currency::Currency;
5use crate::types::decode_error::DecodeError;
6use crate::types::mpt_id::MptId;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
17#[repr(C)]
18pub struct XrpIssue {}
19
20#[derive(Debug, Clone, PartialEq, Eq)]
29#[repr(C)]
30pub struct IouIssue {
31 issuer: AccountID,
32 currency: Currency,
33 _bytes: [u8; 40],
34}
35
36impl IouIssue {
37 pub fn new(issuer: AccountID, currency: Currency) -> Self {
38 let mut bytes = [0u8; 40];
39 bytes[..20].copy_from_slice(currency.as_bytes());
40 bytes[20..].copy_from_slice(&issuer.0);
41 Self {
42 issuer,
43 currency,
44 _bytes: bytes,
45 }
46 }
47
48 pub fn as_bytes(&self) -> &[u8] {
49 &self._bytes
50 }
51}
52
53#[derive(Debug, Clone, Copy, PartialEq, Eq)]
62#[repr(C)]
63pub struct MptIssue {
64 mpt_id: MptId,
65}
66
67impl MptIssue {
68 pub fn new(mpt_id: MptId) -> Self {
69 Self { mpt_id }
70 }
71
72 pub fn mpt_id(&self) -> MptId {
73 self.mpt_id
74 }
75}
76
77#[derive(Debug, Clone, PartialEq, Eq)]
87#[repr(C)]
88pub enum Issue {
89 XRP(XrpIssue),
90 IOU(IouIssue),
91 MPT(MptIssue),
92}
93
94impl Issue {
95 pub fn as_bytes(&self) -> &[u8] {
96 match self {
97 Issue::XRP(_) => {
98 static XRP_BUF: [u8; 20] = [0; 20];
99 &XRP_BUF
100 }
101 Issue::IOU(iou) => iou.as_bytes(),
102 Issue::MPT(mpt) => mpt.mpt_id.as_bytes(),
103 }
104 }
105
106 #[inline]
121 pub fn from_buffer(buffer: [u8; 40], len: usize) -> Result<Self> {
122 match len {
123 20 => Result::Ok(Issue::XRP(XrpIssue {})),
124 24 => {
125 let mpt_bytes: [u8; 24] = buffer[..24].try_into().unwrap_or([0u8; 24]);
126 let mpt_id = MptId::from(mpt_bytes);
127 Result::Ok(Issue::MPT(MptIssue::new(mpt_id)))
128 }
129 40 => {
130 let currency_bytes: [u8; 20] = buffer[..20].try_into().unwrap_or([0u8; 20]);
131 let issuer_bytes: [u8; 20] = buffer[20..40].try_into().unwrap_or([0u8; 20]);
132 let currency = Currency::from(currency_bytes);
133 let issuer = AccountID::from(issuer_bytes);
134 Result::Ok(Issue::IOU(IouIssue::new(issuer, currency)))
135 }
136 _ => Result::Err(crate::host::Error::from_code(len as i32)),
137 }
138 }
139}
140
141impl FieldDecoder for Issue {
146 type Buffer = [u8; 40];
147
148 #[inline]
149 fn empty_buffer() -> Self::Buffer {
150 [0u8; 40]
151 }
152
153 #[inline]
154 fn decode(buf: Self::Buffer, bytes_written: usize) -> core::result::Result<Self, DecodeError> {
155 match Issue::from_buffer(buf, bytes_written) {
156 Result::Ok(issue) => core::result::Result::Ok(issue),
157 Result::Err(_) => core::result::Result::Err(DecodeError),
158 }
159 }
160}
161
162impl FromLedger for Issue {}
163
164#[cfg(test)]
165mod tests {
166 use super::*;
167
168 #[test]
170 fn test_iou_issue_creation() {
171 let issuer = AccountID::from([1u8; 20]);
172 let currency = Currency::from([2u8; 20]);
173 let iou = IouIssue::new(issuer, currency);
174
175 let bytes = iou.as_bytes();
177 assert_eq!(bytes.len(), 40);
178 assert_eq!(&bytes[..20], currency.as_bytes());
179 assert_eq!(&bytes[20..], &issuer.0);
180 }
181
182 #[test]
183 fn test_iou_issue_with_standard_currency() {
184 let issuer = AccountID::from([0xAB; 20]);
185 let currency = Currency::from(*b"USD");
186 let iou = IouIssue::new(issuer, currency);
187
188 let bytes = iou.as_bytes();
189 assert_eq!(&bytes[..20], currency.as_bytes());
191 assert_eq!(&bytes[20..], &issuer.0);
193 }
194
195 #[test]
196 fn test_iou_issue_different_issuers_not_equal() {
197 let issuer1 = AccountID::from([1u8; 20]);
198 let issuer2 = AccountID::from([3u8; 20]);
199 let currency = Currency::from([2u8; 20]);
200
201 let iou1 = IouIssue::new(issuer1, currency);
202 let iou2 = IouIssue::new(issuer2, currency);
203
204 assert_ne!(iou1, iou2);
205 }
206
207 #[test]
209 fn test_mpt_issue_creation() {
210 let issuer = AccountID::from([1u8; 20]);
211 let mpt_id = MptId::new(12345, issuer);
212 let mpt = MptIssue::new(mpt_id);
213
214 assert_eq!(mpt.mpt_id(), mpt_id);
215 }
216
217 #[test]
219 fn test_issue_from_buffer_xrp() {
220 let buffer = [0u8; 40];
221 let result = Issue::from_buffer(buffer, 20);
222 assert!(matches!(result, Result::Ok(Issue::XRP(_))));
223 }
224
225 #[test]
226 fn test_issue_from_buffer_mpt() {
227 let mut buffer = [0u8; 40];
229 buffer[0..4].copy_from_slice(&12345u32.to_be_bytes());
231 buffer[4..24].copy_from_slice(&[0xAB; 20]);
233
234 let result = Issue::from_buffer(buffer, 24);
235 match result {
236 Result::Ok(Issue::MPT(mpt)) => {
237 assert_eq!(mpt.mpt_id().get_sequence_num(), 12345);
238 assert_eq!(mpt.mpt_id().get_issuer(), AccountID::from([0xAB; 20]));
239 }
240 _ => panic!("Expected MPT issue"),
241 }
242 }
243
244 #[test]
245 fn test_issue_from_buffer_iou() {
246 let mut buffer = [0u8; 40];
248 buffer[..20].copy_from_slice(&[0xCC; 20]);
250 buffer[20..40].copy_from_slice(&[0xDD; 20]);
252
253 let result = Issue::from_buffer(buffer, 40);
254 match result {
255 Result::Ok(Issue::IOU(iou)) => {
256 let bytes = iou.as_bytes();
257 assert_eq!(&bytes[..20], &[0xCC; 20]); assert_eq!(&bytes[20..], &[0xDD; 20]); }
260 _ => panic!("Expected IOU issue"),
261 }
262 }
263
264 #[test]
265 fn test_issue_as_bytes() {
266 let xrp = Issue::XRP(XrpIssue {});
267 assert_eq!(xrp.as_bytes(), &[0u8; 20]);
268
269 let issuer = AccountID::from([0xAA; 20]);
270 let currency = Currency::from([0xBB; 20]);
271 let iou = Issue::IOU(IouIssue::new(issuer, currency));
272 assert_eq!(iou.as_bytes().len(), 40);
273
274 let mpt_id = MptId::new(1, AccountID::from([0xCC; 20]));
275 let mpt = Issue::MPT(MptIssue::new(mpt_id));
276 assert_eq!(mpt.as_bytes(), mpt_id.as_bytes());
277 }
278
279 #[test]
280 fn test_issue_from_buffer_invalid_length() {
281 let buffer = [0u8; 40];
282 let result = Issue::from_buffer(buffer, 10);
284 assert!(matches!(result, Result::Err(_)));
285
286 let result = Issue::from_buffer(buffer, 30);
287 assert!(matches!(result, Result::Err(_)));
288
289 let result = Issue::from_buffer(buffer, 0);
290 assert!(matches!(result, Result::Err(_)));
291 }
292}