cosmwasm_vm/
serde.rs

1//! This file simply re-exports some methods from serde_json
2//! The reason is two fold:
3//! 1. To easily ensure that all calling libraries use the same version (minimize code size)
4//! 2. To allow us to switch out to eg. serde-json-core more easily
5use serde::{Deserialize, Serialize};
6use std::any::type_name;
7
8use crate::errors::{VmError, VmResult};
9
10/// Deserializes JSON data into a document of type `T`.
11///
12/// The deserialization limit ensure it is not possible to slow down the execution by
13/// providing overly large JSON documents.
14pub fn from_slice<'a, T>(value: &'a [u8], deserialization_limit: usize) -> VmResult<T>
15where
16    T: Deserialize<'a>,
17{
18    if value.len() > deserialization_limit {
19        return Err(VmError::deserialization_limit_exceeded(
20            value.len(),
21            deserialization_limit,
22        ));
23    }
24    serde_json::from_slice(value).map_err(|e| VmError::parse_err(type_name::<T>(), e))
25}
26
27pub fn to_vec<T>(data: &T) -> VmResult<Vec<u8>>
28where
29    T: Serialize + ?Sized,
30{
31    serde_json::to_vec(data).map_err(|e| VmError::serialize_err(type_name::<T>(), e))
32}
33
34#[cfg(test)]
35mod tests {
36    use super::*;
37    use serde::Deserialize;
38
39    const LIMIT: usize = 20_000;
40
41    #[derive(Serialize, Deserialize, Debug, PartialEq)]
42    #[serde(rename_all = "snake_case")]
43    enum SomeMsg {
44        Refund {},
45        ReleaseAll {
46            image: String,
47            amount: u32,
48            time: u64,
49            karma: i32,
50        },
51        Cowsay {
52            text: String,
53        },
54    }
55
56    #[test]
57    fn from_slice_works() {
58        let deserialized: SomeMsg = from_slice(br#"{"refund":{}}"#, LIMIT).unwrap();
59        assert_eq!(deserialized, SomeMsg::Refund {});
60
61        let deserialized: SomeMsg = from_slice(
62            br#"{"release_all":{"image":"foo","amount":42,"time":18446744073709551615,"karma":-17}}"#, LIMIT
63        )
64        .unwrap();
65        assert_eq!(
66            deserialized,
67            SomeMsg::ReleaseAll {
68                image: "foo".to_string(),
69                amount: 42,
70                time: 18446744073709551615,
71                karma: -17
72            }
73        );
74    }
75
76    #[test]
77    fn from_slice_works_for_special_chars() {
78        let deserialized: SomeMsg =
79            from_slice(br#"{"cowsay":{"text":"foo\"bar\\\"bla"}}"#, LIMIT).unwrap();
80        assert_eq!(
81            deserialized,
82            SomeMsg::Cowsay {
83                text: "foo\"bar\\\"bla".to_string(),
84            }
85        );
86    }
87
88    #[test]
89    fn from_slice_errors_when_exceeding_deserialization_limit() {
90        let result = from_slice::<SomeMsg>(br#"{"refund":{}}"#, 5);
91        match result.unwrap_err() {
92            VmError::DeserializationLimitExceeded {
93                length, max_length, ..
94            } => {
95                assert_eq!(length, 13);
96                assert_eq!(max_length, 5);
97            }
98            err => panic!("Unexpected error: {err:?}"),
99        }
100    }
101
102    #[test]
103    fn to_vec_works() {
104        let msg = SomeMsg::Refund {};
105        let serialized = to_vec(&msg).unwrap();
106        assert_eq!(serialized, br#"{"refund":{}}"#);
107
108        let msg = SomeMsg::ReleaseAll {
109            image: "foo".to_string(),
110            amount: 42,
111            time: 9007199254740999, // Number.MAX_SAFE_INTEGER + 7
112            karma: -17,
113        };
114        let serialized = String::from_utf8(to_vec(&msg).unwrap()).unwrap();
115        assert_eq!(
116            serialized,
117            r#"{"release_all":{"image":"foo","amount":42,"time":9007199254740999,"karma":-17}}"#
118        );
119    }
120
121    #[test]
122    fn to_vec_works_for_special_chars() {
123        let msg = SomeMsg::Cowsay {
124            text: "foo\"bar\\\"bla".to_string(),
125        };
126        let serialized = String::from_utf8(to_vec(&msg).unwrap()).unwrap();
127        assert_eq!(serialized, r#"{"cowsay":{"text":"foo\"bar\\\"bla"}}"#);
128    }
129}