Skip to main content

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