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