iota_types/
iota_serde.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2024 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5use std::{
6    fmt,
7    fmt::{Debug, Display, Formatter, Write},
8    marker::PhantomData,
9    ops::Deref,
10    str::FromStr,
11};
12
13use iota_protocol_config::ProtocolVersion;
14use serde::{
15    self, Deserialize, Serialize,
16    de::{Deserializer, Error},
17    ser::{Error as SerError, Serializer},
18};
19use serde_with::{Bytes, DeserializeAs, DisplayFromStr, SerializeAs, serde_as};
20
21use crate::{
22    base_types::{IotaAddress, StructTag, TypeTag},
23    parse_iota_struct_tag, parse_iota_type_tag,
24};
25
26#[inline]
27pub(crate) fn to_custom_deser_error<'de, D, E>(e: E) -> D::Error
28where
29    E: Debug,
30    D: Deserializer<'de>,
31{
32    Error::custom(format!("byte deserialization failed, cause by: {e:?}"))
33}
34
35#[inline]
36pub(crate) fn to_custom_ser_error<S, E>(e: E) -> S::Error
37where
38    E: Debug,
39    S: Serializer,
40{
41    S::Error::custom(format!("byte serialization failed, cause by: {e:?}"))
42}
43
44/// Use with serde_as to control serde for human-readable serialization and
45/// deserialization `H` : serde_as SerializeAs/DeserializeAs delegation for
46/// human readable in/output `R` : serde_as SerializeAs/DeserializeAs delegation
47/// for non-human readable in/output
48///
49/// # Example:
50///
51/// ```text
52/// #[serde_as]
53/// #[derive(Deserialize, Serialize)]
54/// struct Example(#[serde_as(as = "Readable<DisplayFromStr, _>")] [u8; 20]);
55/// ```
56///
57/// The above example will delegate human-readable serde to `DisplayFromStr`
58/// and array tuple (default) for non-human-readable serializer.
59pub struct Readable<H, R> {
60    human_readable: PhantomData<H>,
61    non_human_readable: PhantomData<R>,
62}
63
64impl<T: ?Sized, H, R> SerializeAs<T> for Readable<H, R>
65where
66    H: SerializeAs<T>,
67    R: SerializeAs<T>,
68{
69    fn serialize_as<S>(value: &T, serializer: S) -> Result<S::Ok, S::Error>
70    where
71        S: Serializer,
72    {
73        if serializer.is_human_readable() {
74            H::serialize_as(value, serializer)
75        } else {
76            R::serialize_as(value, serializer)
77        }
78    }
79}
80
81impl<'de, R, H, T> DeserializeAs<'de, T> for Readable<H, R>
82where
83    H: DeserializeAs<'de, T>,
84    R: DeserializeAs<'de, T>,
85{
86    fn deserialize_as<D>(deserializer: D) -> Result<T, D::Error>
87    where
88        D: Deserializer<'de>,
89    {
90        if deserializer.is_human_readable() {
91            H::deserialize_as(deserializer)
92        } else {
93            R::deserialize_as(deserializer)
94        }
95    }
96}
97
98pub struct IotaStructTag;
99
100impl SerializeAs<StructTag> for IotaStructTag {
101    fn serialize_as<S>(value: &StructTag, serializer: S) -> Result<S::Ok, S::Error>
102    where
103        S: Serializer,
104    {
105        let f = to_iota_struct_tag_string(value).map_err(S::Error::custom)?;
106        f.serialize(serializer)
107    }
108}
109
110const IOTA_ADDRESSES: [IotaAddress; 7] = [
111    IotaAddress::ZERO,
112    IotaAddress::STD,
113    IotaAddress::FRAMEWORK,
114    IotaAddress::SYSTEM,
115    IotaAddress::STARDUST,
116    IotaAddress::SYSTEM_STATE,
117    IotaAddress::CLOCK,
118];
119/// Serialize StructTag as a string, retaining the leading zeros in the address.
120pub fn to_iota_struct_tag_string(value: &StructTag) -> Result<String, fmt::Error> {
121    let mut f = String::new();
122    let address = value.address();
123    // trim leading zeros if address is in IOTA_ADDRESSES
124    let address_str = if IOTA_ADDRESSES.contains(&address) {
125        address.to_short_hex()
126    } else {
127        address.to_canonical_string(/* with_prefix */ true)
128    };
129
130    write!(f, "{}::{}::{}", address_str, value.module(), value.name())?;
131    if let Some(first_ty) = value.type_params().first() {
132        write!(f, "<")?;
133        write!(f, "{}", to_iota_type_tag_string(first_ty)?)?;
134        for ty in value.type_params().iter().skip(1) {
135            write!(f, ", {}", to_iota_type_tag_string(ty)?)?;
136        }
137        write!(f, ">")?;
138    }
139    Ok(f)
140}
141
142fn to_iota_type_tag_string(value: &TypeTag) -> Result<String, fmt::Error> {
143    match value {
144        TypeTag::Vector(t) => Ok(format!("vector<{}>", to_iota_type_tag_string(t)?)),
145        TypeTag::Struct(s) => to_iota_struct_tag_string(s),
146        _ => Ok(value.to_string()),
147    }
148}
149
150impl<'de> DeserializeAs<'de, StructTag> for IotaStructTag {
151    fn deserialize_as<D>(deserializer: D) -> Result<StructTag, D::Error>
152    where
153        D: Deserializer<'de>,
154    {
155        let s = String::deserialize(deserializer)?;
156        parse_iota_struct_tag(&s).map_err(D::Error::custom)
157    }
158}
159
160pub struct IotaTypeTag;
161
162impl SerializeAs<TypeTag> for IotaTypeTag {
163    fn serialize_as<S>(value: &TypeTag, serializer: S) -> Result<S::Ok, S::Error>
164    where
165        S: Serializer,
166    {
167        let s = to_iota_type_tag_string(value).map_err(S::Error::custom)?;
168        s.serialize(serializer)
169    }
170}
171
172impl<'de> DeserializeAs<'de, TypeTag> for IotaTypeTag {
173    fn deserialize_as<D>(deserializer: D) -> Result<TypeTag, D::Error>
174    where
175        D: Deserializer<'de>,
176    {
177        let s = String::deserialize(deserializer)?;
178        parse_iota_type_tag(&s).map_err(D::Error::custom)
179    }
180}
181
182/// A marker for type tags that are serialized as strings. Normally, a
183/// type tag is serialized as a string for readable formats, and as a byte array
184/// for non-readable formats. This marker can be used to serialize a type tag as
185/// a string even in non-readable formats.
186pub struct TypeName;
187
188impl SerializeAs<TypeTag> for TypeName {
189    fn serialize_as<S>(value: &TypeTag, serializer: S) -> Result<S::Ok, S::Error>
190    where
191        S: Serializer,
192    {
193        let s = value.to_canonical_string(false);
194        s.serialize(serializer)
195    }
196}
197
198impl<'de> DeserializeAs<'de, TypeTag> for TypeName {
199    fn deserialize_as<D>(deserializer: D) -> Result<TypeTag, D::Error>
200    where
201        D: Deserializer<'de>,
202    {
203        let s = String::deserialize(deserializer)?;
204        parse_iota_type_tag(&s).map_err(D::Error::custom)
205    }
206}
207
208#[serde_as]
209#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Copy)]
210pub struct BigInt<T>(#[serde_as(as = "DisplayFromStr")] T)
211where
212    T: Display + FromStr,
213    <T as FromStr>::Err: Display;
214
215impl<T> BigInt<T>
216where
217    T: Display + FromStr,
218    <T as FromStr>::Err: Display,
219{
220    pub fn into_inner(self) -> T {
221        self.0
222    }
223}
224
225impl<T> SerializeAs<T> for BigInt<T>
226where
227    T: Display + FromStr + Copy,
228    <T as FromStr>::Err: Display,
229{
230    fn serialize_as<S>(value: &T, serializer: S) -> Result<S::Ok, S::Error>
231    where
232        S: Serializer,
233    {
234        BigInt(*value).serialize(serializer)
235    }
236}
237
238impl<'de, T> DeserializeAs<'de, T> for BigInt<T>
239where
240    T: Display + FromStr + Copy,
241    <T as FromStr>::Err: Display,
242{
243    fn deserialize_as<D>(deserializer: D) -> Result<T, D::Error>
244    where
245        D: Deserializer<'de>,
246    {
247        Ok(*BigInt::deserialize(deserializer)?)
248    }
249}
250
251impl<T> From<T> for BigInt<T>
252where
253    T: Display + FromStr,
254    <T as FromStr>::Err: Display,
255{
256    fn from(v: T) -> BigInt<T> {
257        BigInt(v)
258    }
259}
260
261impl<T> Deref for BigInt<T>
262where
263    T: Display + FromStr,
264    <T as FromStr>::Err: Display,
265{
266    type Target = T;
267
268    fn deref(&self) -> &Self::Target {
269        &self.0
270    }
271}
272
273impl<T> Display for BigInt<T>
274where
275    T: Display + FromStr,
276    <T as FromStr>::Err: Display,
277{
278    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
279        write!(f, "{}", self.0)
280    }
281}
282
283#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Copy)]
284pub struct SequenceNumber(u64);
285
286impl SerializeAs<crate::base_types::SequenceNumber> for SequenceNumber {
287    fn serialize_as<S>(
288        value: &crate::base_types::SequenceNumber,
289        serializer: S,
290    ) -> Result<S::Ok, S::Error>
291    where
292        S: Serializer,
293    {
294        let s = value.to_string();
295        s.serialize(serializer)
296    }
297}
298
299impl<'de> DeserializeAs<'de, crate::base_types::SequenceNumber> for SequenceNumber {
300    fn deserialize_as<D>(deserializer: D) -> Result<crate::base_types::SequenceNumber, D::Error>
301    where
302        D: Deserializer<'de>,
303    {
304        let b = BigInt::deserialize(deserializer)?;
305        Ok(crate::base_types::SequenceNumber::from_u64(*b))
306    }
307}
308
309#[serde_as]
310#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Copy)]
311#[serde(rename = "ProtocolVersion")]
312pub struct AsProtocolVersion(u64);
313
314impl SerializeAs<ProtocolVersion> for AsProtocolVersion {
315    fn serialize_as<S>(value: &ProtocolVersion, serializer: S) -> Result<S::Ok, S::Error>
316    where
317        S: Serializer,
318    {
319        let s = value.as_u64().to_string();
320        s.serialize(serializer)
321    }
322}
323
324impl<'de> DeserializeAs<'de, ProtocolVersion> for AsProtocolVersion {
325    fn deserialize_as<D>(deserializer: D) -> Result<ProtocolVersion, D::Error>
326    where
327        D: Deserializer<'de>,
328    {
329        let b = BigInt::<u64>::deserialize(deserializer)?;
330        Ok(ProtocolVersion::from(*b))
331    }
332}
333
334/// Serializes and deserializes a RoaringBitmap with its own on-disk standard.
335/// <https://github.com/RoaringBitmap/RoaringFormatSpec>
336pub(crate) struct IotaBitmap;
337
338impl SerializeAs<roaring::RoaringBitmap> for IotaBitmap {
339    fn serialize_as<S>(source: &roaring::RoaringBitmap, serializer: S) -> Result<S::Ok, S::Error>
340    where
341        S: Serializer,
342    {
343        let mut bytes = vec![];
344
345        source
346            .serialize_into(&mut bytes)
347            .map_err(to_custom_ser_error::<S, _>)?;
348        Bytes::serialize_as(&bytes, serializer)
349    }
350}
351
352impl<'de> DeserializeAs<'de, roaring::RoaringBitmap> for IotaBitmap {
353    fn deserialize_as<D>(deserializer: D) -> Result<roaring::RoaringBitmap, D::Error>
354    where
355        D: Deserializer<'de>,
356    {
357        let bytes: Vec<u8> = Bytes::deserialize_as(deserializer)?;
358        deserialize_iota_bitmap(&bytes).map_err(to_custom_deser_error::<'de, D, _>)
359    }
360}
361
362// Deserializes a RoaringBitmap.
363// NOTE: roaring 0.11+ validates container key ordering in deserialize_from(),
364// so bitmaps with unsorted/duplicate container keys are already rejected at the
365// deserialization level.
366fn deserialize_iota_bitmap(bytes: &[u8]) -> std::io::Result<roaring::RoaringBitmap> {
367    roaring::RoaringBitmap::deserialize_from(bytes)
368}
369
370#[cfg(test)]
371mod test {
372    use base64::Engine as _;
373
374    use super::*;
375
376    #[test]
377    fn test_iota_bitmap_rejects_unsorted_keys() {
378        // This base64 encodes a malformed roaring bitmap with unsorted/duplicate
379        // container keys. roaring 0.11+ rejects such bitmaps at deserialization.
380        let raw = "OjAAAAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWAAAAFoAAABcAAAAXgAAAGAAAABiAAAAZAAAAGYAAABoAAAAagAAAAEAAQABAAEAAQABAAEAAQABAAEA";
381        let bytes = base64::engine::general_purpose::STANDARD
382            .decode(raw)
383            .unwrap();
384
385        assert!(deserialize_iota_bitmap(&bytes[..]).is_err());
386    }
387
388    #[test]
389    fn test_iota_bitmap_valid_roundtrip() {
390        // Build a valid bitmap, serialize it, then deserialize via our function.
391        let mut original = roaring::RoaringBitmap::new();
392        original.insert(1);
393        original.insert(42);
394        original.insert(100);
395
396        let mut bytes = vec![];
397        original.serialize_into(&mut bytes).unwrap();
398
399        let result = deserialize_iota_bitmap(&bytes[..]).unwrap();
400        assert_eq!(result.len(), 3);
401        let values: Vec<u32> = result.iter().collect();
402        assert_eq!(values, vec![1, 42, 100]);
403    }
404}