xrpl/core/binarycodec/types/
mod.rs1pub mod account_id;
4pub mod amount;
5pub mod blob;
6pub mod currency;
7pub mod exceptions;
8pub mod hash;
9pub mod issue;
10pub mod paths;
11pub(crate) mod test_cases;
12pub mod utils;
13pub mod vector256;
14pub mod xchain_bridge;
15
16use core::convert::TryFrom;
17use core::convert::TryInto;
18use core::fmt::Debug;
19use core::fmt::Display;
20use core::iter::FromIterator;
21
22pub use self::account_id::AccountId;
23pub use self::amount::Amount;
24pub use self::blob::Blob;
25pub use self::currency::Currency;
26pub use self::hash::Hash;
27pub use self::hash::Hash128;
28pub use self::hash::Hash160;
29pub use self::hash::Hash256;
30pub use self::issue::Issue;
31pub use self::paths::Path;
32pub use self::paths::PathSet;
33pub use self::paths::PathStep;
34pub use self::vector256::Vector256;
35pub use self::xchain_bridge::XChainBridge;
36
37use crate::core::binarycodec::binary_wrappers::Serialization;
38use crate::core::binarycodec::definitions::get_field_instance;
39use crate::core::binarycodec::definitions::get_transaction_result_code;
40use crate::core::binarycodec::definitions::get_transaction_type_code;
41use crate::core::binarycodec::definitions::FieldInstance;
42use crate::core::exceptions::XRPLCoreResult;
43use crate::core::BinaryParser;
44use alloc::borrow::Cow;
45use alloc::borrow::ToOwned;
46use alloc::string::String;
47use alloc::string::ToString;
48use alloc::vec;
49use alloc::vec::Vec;
50use amount::IssuedCurrency;
51use exceptions::XRPLTypeException;
52use serde::Deserialize;
53use serde_json::Map;
54use serde_json::Value;
55
56use super::BinarySerializer;
57use crate::core::addresscodec::is_valid_xaddress;
58use crate::core::addresscodec::xaddress_to_classic_address;
59
60const ACCOUNT: &str = "Account";
61const SOURCE_TAG: &str = "SourceTag";
62const DESTINATION: &str = "Destination";
63const DESTINATION_TAG: &str = "DestinationTag";
64const UNL_MODIFY_TX_TYPE: &str = "0066";
65const ST_OBJECT: &str = "STObject";
66const OBJECT_END_MARKER_BYTES: [u8; 1] = [0xE1];
67const ARRAY_END_MARKER: [u8; 1] = [0xF1];
68
69#[derive(Debug)]
70pub enum XRPLTypes {
71 AccountID(AccountId),
72 Amount(Amount),
73 Blob(Blob),
74 Currency(Currency),
75 Hash128(Hash128),
76 Hash160(Hash160),
77 Hash256(Hash256),
78 Issue(Issue),
79 Path(Path),
80 PathSet(PathSet),
81 PathStep(PathStep),
82 Vector256(Vector256),
83 STArray(STArray),
84 STObject(STObject),
85 UInt8(u8),
86 UInt16(u16),
87 UInt32(u32),
88 UInt64(u64),
89 XChainBridge(XChainBridge),
90 Unknown,
91}
92
93impl XRPLTypes {
94 pub fn from_value(name: &str, value: Value) -> XRPLCoreResult<Option<XRPLTypes>> {
95 if value.is_null() {
96 Ok(None)
97 } else if let Some(value) = value.as_str() {
98 match name {
100 "AccountID" => Ok(Some(XRPLTypes::AccountID(Self::type_from_str(value)?))),
101 "Amount" => Ok(Some(XRPLTypes::Amount(Self::type_from_str(value)?))),
102 "Blob" => Ok(Some(XRPLTypes::Blob(Self::type_from_str(value)?))),
103 "Currency" => Ok(Some(XRPLTypes::Currency(Self::type_from_str(value)?))),
104 "Hash128" => Ok(Some(XRPLTypes::Hash128(Self::type_from_str(value)?))),
105 "Hash160" => Ok(Some(XRPLTypes::Hash160(Self::type_from_str(value)?))),
106 "Hash256" => Ok(Some(XRPLTypes::Hash256(Self::type_from_str(value)?))),
107 "XChainClaimID" => Ok(Some(XRPLTypes::Hash256(Self::type_from_str(value)?))),
108 "UInt8" => Ok(Some(XRPLTypes::UInt8(
109 value
110 .parse::<u8>()
111 .map_err(XRPLTypeException::ParseIntError)?,
112 ))),
113 "UInt16" => Ok(Some(XRPLTypes::UInt16(
114 value
115 .parse::<u16>()
116 .map_err(XRPLTypeException::ParseIntError)?,
117 ))),
118 "UInt32" => Ok(Some(XRPLTypes::UInt32(
119 value
120 .parse::<u32>()
121 .map_err(XRPLTypeException::ParseIntError)?,
122 ))),
123 "UInt64" => Ok(Some(XRPLTypes::UInt64(
124 value
125 .parse::<u64>()
126 .map_err(XRPLTypeException::ParseIntError)?,
127 ))),
128 _ => Err(exceptions::XRPLTypeException::UnknownXRPLType.into()),
129 }
130 } else if let Some(value) = value.as_u64() {
131 match name {
133 "UInt8" => Ok(Some(XRPLTypes::UInt8(value as u8))),
134 "UInt16" => Ok(Some(XRPLTypes::UInt16(value as u16))),
135 "UInt32" => Ok(Some(XRPLTypes::UInt32(value as u32))),
136 "UInt64" => Ok(Some(XRPLTypes::UInt64(value))),
137 _ => Err(exceptions::XRPLTypeException::UnknownXRPLType.into()),
138 }
139 } else if let Some(value) = value.as_object() {
140 match name {
142 "Amount" => Ok(Some(XRPLTypes::Amount(Self::amount_from_map(
143 value.to_owned(),
144 )?))),
145 "STObject" => Ok(Some(XRPLTypes::STObject(STObject::try_from_value(
146 Value::Object(value.to_owned()),
147 false,
148 )?))),
149 "XChainBridge" => Ok(Some(XRPLTypes::XChainBridge(XChainBridge::try_from(
150 Value::Object(value.to_owned()),
151 )?))),
152 _ => Err(exceptions::XRPLTypeException::UnknownXRPLType.into()),
153 }
154 } else if let Some(value) = value.as_array() {
155 match name {
157 "STArray" => Ok(Some(XRPLTypes::STArray(STArray::try_from_value(
158 Value::Array(value.to_owned()),
159 )?))),
160 _ => Err(exceptions::XRPLTypeException::UnknownXRPLType.into()),
161 }
162 } else {
163 Err(exceptions::XRPLTypeException::UnknownXRPLType.into())
165 }
166 }
167
168 fn type_from_str<'a, T>(value: &'a str) -> XRPLCoreResult<T>
169 where
170 T: TryFrom<&'a str>,
171 <T as TryFrom<&'a str>>::Error: Display,
172 {
173 value
174 .try_into()
175 .map_err(|_| XRPLTypeException::TryFromStrError.into())
176 }
177
178 fn amount_from_map<T>(value: Map<String, Value>) -> XRPLCoreResult<T>
179 where
180 T: TryFrom<IssuedCurrency>,
181 <T as TryFrom<IssuedCurrency>>::Error: Display,
182 {
183 match IssuedCurrency::try_from(Value::Object(value)) {
184 Ok(value) => value
185 .try_into()
186 .map_err(|_| XRPLTypeException::TryFromIssuedCurrencyError.into()),
187 Err(error) => Err(error),
188 }
189 }
190}
191
192impl From<XRPLTypes> for SerializedType {
193 fn from(val: XRPLTypes) -> Self {
194 match val {
195 XRPLTypes::AccountID(account_id) => SerializedType::from(account_id),
196 XRPLTypes::Amount(amount) => SerializedType::from(amount),
197 XRPLTypes::Blob(blob) => SerializedType::from(blob),
198 XRPLTypes::Currency(currency) => SerializedType::from(currency),
199 XRPLTypes::Hash128(hash128) => SerializedType::from(hash128),
200 XRPLTypes::Hash160(hash160) => SerializedType::from(hash160),
201 XRPLTypes::Hash256(hash256) => SerializedType::from(hash256),
202 XRPLTypes::Path(path) => SerializedType::from(path),
203 XRPLTypes::PathSet(path_set) => SerializedType::from(path_set),
204 XRPLTypes::PathStep(path_step) => SerializedType::from(path_step),
205 XRPLTypes::Vector256(vector256) => SerializedType::from(vector256),
206 XRPLTypes::STArray(st_array) => st_array.0,
207 XRPLTypes::STObject(st_object) => st_object.0,
208 XRPLTypes::UInt8(value) => SerializedType(value.to_be_bytes().to_vec()),
209 XRPLTypes::UInt16(value) => SerializedType(value.to_be_bytes().to_vec()),
210 XRPLTypes::UInt32(value) => SerializedType(value.to_be_bytes().to_vec()),
211 XRPLTypes::UInt64(value) => SerializedType(value.to_be_bytes().to_vec()),
212 XRPLTypes::XChainBridge(x_chain_bridge) => SerializedType::from(x_chain_bridge),
213 XRPLTypes::Issue(issue) => SerializedType::from(issue),
214 XRPLTypes::Unknown => SerializedType(vec![]),
215 }
216 }
217}
218
219#[derive(Debug, Deserialize, Clone)]
221pub struct SerializedType(Vec<u8>);
222
223#[derive(Debug)]
228pub struct STArray(SerializedType);
229
230impl STArray {
231 pub fn try_from_value(value: Value) -> XRPLCoreResult<Self> {
254 if let Some(array) = value.as_array() {
255 if !array.is_empty() && array.iter().filter(|v| v.is_object()).count() != array.len() {
256 Err(exceptions::XRPLSerializeArrayException::ExpectedObjectArray.into())
257 } else {
258 let mut serializer = BinarySerializer::new();
259 for object in array {
260 let obj = match object {
261 Value::Object(map) => map,
262 _ => {
263 return Err(
264 exceptions::XRPLSerializeArrayException::ExpectedObjectArray.into(),
265 )
266 }
267 };
268 let transaction = STObject::try_from_value(Value::Object(obj.clone()), false)?;
269 serializer.append(transaction.as_ref().to_vec().as_mut());
270 }
271 serializer.append(ARRAY_END_MARKER.to_vec().as_mut());
272 Ok(STArray(serializer.into()))
273 }
274 } else {
275 Err(exceptions::XRPLSerializeArrayException::ExpectedArray.into())
276 }
277 }
278}
279
280impl XRPLType for STArray {
281 type Error = XRPLTypeException;
282
283 fn new(buffer: Option<&[u8]>) -> XRPLCoreResult<Self, Self::Error> {
284 if let Some(data) = buffer {
285 Ok(STArray(SerializedType(data.to_vec())))
286 } else {
287 Ok(STArray(SerializedType(vec![])))
288 }
289 }
290}
291
292impl AsRef<[u8]> for STArray {
293 fn as_ref(&self) -> &[u8] {
294 self.0.as_ref()
295 }
296}
297
298#[derive(Debug)]
303pub struct STObject(SerializedType);
304
305impl STObject {
306 pub fn try_from_value(value: Value, signing_only: bool) -> XRPLCoreResult<Self> {
341 let object = match value {
342 Value::Object(map) => map,
343 _ => return Err(exceptions::XRPLSerializeMapException::ExpectedObject.into()),
344 };
345 let mut serializer = BinarySerializer::new();
346 let mut value_xaddress_handled = Map::new();
347 for (field, value) in &object {
348 if let Some(value) = value.as_str() {
349 if is_valid_xaddress(value) {
350 let handled_xaddress = handle_xaddress(field.into(), value.into())?;
351 if let Some(handled_tag) = handled_xaddress.get(SOURCE_TAG) {
352 if let Some(object_tag) = object.get(SOURCE_TAG) {
353 if handled_tag != object_tag {
354 return Err(
355 exceptions::XRPLSerializeMapException::AccountMismatchingTags
356 .into(),
357 );
358 }
359 }
360 }
361 if let Some(handled_tag) = handled_xaddress.get(DESTINATION_TAG) {
362 if let Some(object_tag) = object.get(DESTINATION_TAG) {
363 if handled_tag != object_tag {
364 return Err(
365 exceptions::XRPLSerializeMapException::DestinationMismatchingTags.into()
366 );
367 }
368 }
369 }
370 value_xaddress_handled.extend(handled_xaddress);
371 } else if field == "TransactionType" {
372 let transaction_type_code = match get_transaction_type_code(value) {
373 Some(code) => code,
374 None => {
375 return Err(
376 exceptions::XRPLSerializeMapException::UnknownTransactionType(
377 value.to_string(),
378 )
379 .into(),
380 )
381 }
382 };
383 value_xaddress_handled.insert(
384 field.to_owned(),
385 Value::Number(transaction_type_code.to_owned().into()),
386 );
387 } else if field == "TransactionResult" {
388 let transaction_result_code =
389 match get_transaction_result_code(value) {
390 Some(code) => code,
391 None => return Err(
392 exceptions::XRPLSerializeMapException::UnknownTransactionResult(
393 value.to_string(),
394 )
395 .into(),
396 ),
397 };
398 value_xaddress_handled.insert(
399 field.to_owned(),
400 Value::Number(transaction_result_code.to_owned().into()),
401 );
402 } else if field == "LedgerEntryType" {
403 let ledger_entry_type_code = match get_transaction_type_code(value) {
404 Some(code) => code,
405 None => {
406 return Err(
407 exceptions::XRPLSerializeMapException::UnknownLedgerEntryType(
408 value.to_string(),
409 )
410 .into(),
411 )
412 }
413 };
414 value_xaddress_handled.insert(
415 field.to_owned(),
416 Value::Number(ledger_entry_type_code.to_owned().into()),
417 );
418 } else {
419 value_xaddress_handled
420 .insert(field.to_owned(), Value::String(value.to_owned()));
421 }
422 } else {
423 value_xaddress_handled.insert(field.to_owned(), value.clone());
424 }
425 }
426
427 let mut sorted_keys: Vec<FieldInstance> = Vec::new();
428 for (field, _) in &value_xaddress_handled {
429 let field_instance = get_field_instance(field);
430 if let Some(field_instance) = field_instance {
431 if value_xaddress_handled.contains_key(&field_instance.name)
432 && field_instance.is_serialized
433 {
434 sorted_keys.push(field_instance);
435 }
436 }
437 }
438 sorted_keys.sort_by_key(|k| k.ordinal);
439 if signing_only {
440 sorted_keys.retain(|k| k.is_signing);
441 }
442 let mut is_unl_modify = false;
443
444 for field_instance in &sorted_keys {
445 let associated_value = value_xaddress_handled.get(&field_instance.name).ok_or(
446 exceptions::XRPLTypeException::MissingField(field_instance.name.clone()),
447 )?;
448 let associated_value = XRPLTypes::from_value(
449 &field_instance.associated_type,
450 associated_value.to_owned(),
451 )?;
452 if associated_value.is_none() {
453 continue;
454 }
455 let associated_value = associated_value.unwrap(); let associated_value: SerializedType = associated_value.into();
457 if field_instance.name == "TransactionType"
462 && associated_value.to_string() == UNL_MODIFY_TX_TYPE
463 {
464 is_unl_modify = true;
465 }
466 let is_unl_modify_workaround = field_instance.name == "Account" && is_unl_modify;
467
468 serializer.write_field_and_value(
469 field_instance.to_owned(),
470 associated_value.as_ref(),
471 is_unl_modify_workaround,
472 );
473 if field_instance.associated_type == ST_OBJECT {
474 serializer.append(OBJECT_END_MARKER_BYTES.to_vec().as_mut());
475 }
476 }
477
478 Ok(STObject(serializer.into()))
479 }
480}
481
482impl XRPLType for STObject {
483 type Error = XRPLTypeException;
484
485 fn new(buffer: Option<&[u8]>) -> XRPLCoreResult<Self, Self::Error> {
486 if let Some(data) = buffer {
487 Ok(STObject(SerializedType(data.to_vec())))
488 } else {
489 Ok(STObject(SerializedType(vec![])))
490 }
491 }
492}
493
494impl AsRef<[u8]> for STObject {
495 fn as_ref(&self) -> &[u8] {
496 self.0.as_ref()
497 }
498}
499
500fn handle_xaddress(field: Cow<str>, xaddress: Cow<str>) -> XRPLCoreResult<Map<String, Value>> {
501 let (classic_address, tag, _is_test_net) = xaddress_to_classic_address(&xaddress)?;
502 if let Some(tag) = tag {
503 if field == DESTINATION {
504 let tag_name = DESTINATION_TAG;
505 Ok(Map::from_iter(vec![
506 (field.to_string(), Value::String(classic_address)),
507 (tag_name.to_string(), Value::Number(tag.into())),
508 ]))
509 } else if field == ACCOUNT {
510 let tag_name = SOURCE_TAG;
511 Ok(Map::from_iter(vec![
512 (field.to_string(), Value::String(classic_address)),
513 (tag_name.to_string(), Value::Number(tag.into())),
514 ]))
515 } else {
516 Err(exceptions::XRPLSerializeMapException::DisallowedTag {
517 field: field.to_string(),
518 }
519 .into())
520 }
521 } else {
522 Ok(Map::from_iter(vec![(
523 field.to_string(),
524 Value::String(classic_address),
525 )]))
526 }
527}
528
529pub trait XRPLType {
555 type Error;
557
558 fn new(buffer: Option<&[u8]>) -> XRPLCoreResult<Self, Self::Error>
560 where
561 Self: Sized;
562}
563
564pub trait TryFromParser {
590 type Error;
592
593 fn from_parser(
595 parser: &mut BinaryParser,
596 length: Option<usize>,
597 ) -> XRPLCoreResult<Self, Self::Error>
598 where
599 Self: Sized;
600}
601
602impl Display for SerializedType {
603 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
605 write!(f, "{}", hex::encode_upper(self.0.as_slice()))
606 }
607}
608
609impl From<Vec<u8>> for SerializedType {
610 fn from(buffer: Vec<u8>) -> Self {
612 SerializedType(buffer)
613 }
614}
615
616impl AsRef<[u8]> for SerializedType {
617 fn as_ref(&self) -> &[u8] {
619 self.0.as_slice()
620 }
621}
622
623impl<T> From<T> for SerializedType
624where
625 T: XRPLType + AsRef<[u8]>,
626{
627 fn from(instance: T) -> Self {
629 SerializedType(instance.as_ref().to_vec())
630 }
631}