sui_types/
proto_value.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use move_core_types::annotated_value as A;
5use move_core_types::annotated_visitor as AV;
6use prost_types::Struct;
7use prost_types::Value;
8use prost_types::value::Kind;
9
10use crate::object::option_visitor as OV;
11use crate::object::rpc_visitor as RV;
12
13/// This is the maximum depth of a proto message
14/// The maximum depth of a proto message is 100. Given this value may be nested itself somewhere
15/// we'll conservitively cap this to ~80% of that.
16const MAX_DEPTH: usize = 80;
17
18pub struct ProtoVisitor {
19    /// Budget to spend on visiting.
20    bound: usize,
21}
22
23pub struct ProtoWriter<'b> {
24    bound: &'b mut usize,
25    depth: usize,
26}
27
28#[derive(thiserror::Error, Debug)]
29pub enum Error {
30    #[error(transparent)]
31    Visitor(#[from] AV::Error),
32
33    #[error("Deserialized value too large")]
34    OutOfBudget,
35
36    #[error("Exceeded maximum depth")]
37    TooNested,
38
39    #[error("Unexpected type")]
40    UnexpectedType,
41}
42
43impl ProtoVisitor {
44    pub fn new(bound: usize) -> Self {
45        Self { bound }
46    }
47
48    /// Deserialize `bytes` as a `MoveValue` with layout `layout`. Can fail if the bytes do not
49    /// represent a value with this layout, or if the deserialized value exceeds the field/type size
50    /// budget.
51    pub fn deserialize_value(
52        mut self,
53        bytes: &[u8],
54        layout: &A::MoveTypeLayout,
55    ) -> Result<Value, Error> {
56        A::MoveValue::visit_deserialize(
57            bytes,
58            layout,
59            &mut RV::RpcVisitor::new(ProtoWriter {
60                bound: &mut self.bound,
61                depth: 0,
62            }),
63        )
64    }
65}
66
67impl ProtoWriter<'_> {
68    fn debit(&mut self, size: usize) -> Result<(), Error> {
69        if *self.bound < size {
70            Err(Error::OutOfBudget)
71        } else {
72            *self.bound -= size;
73            Ok(())
74        }
75    }
76
77    fn debit_value(&mut self) -> Result<(), Error> {
78        self.debit(size_of::<Value>())
79    }
80
81    fn debit_str(&mut self, s: &str) -> Result<(), Error> {
82        self.debit(s.len())
83    }
84
85    fn debit_string_value(&mut self, s: &str) -> Result<(), Error> {
86        self.debit_str(s)?;
87        self.debit_value()
88    }
89}
90
91impl<'b> RV::Writer for ProtoWriter<'b> {
92    type Value = Value;
93    type Error = Error;
94
95    type Vec = Vec<Value>;
96    type Map = Struct;
97
98    type Nested<'a>
99        = ProtoWriter<'a>
100    where
101        Self: 'a;
102
103    fn nest(&mut self) -> Result<Self::Nested<'_>, Self::Error> {
104        if self.depth >= MAX_DEPTH {
105            Err(Error::TooNested)
106        } else {
107            Ok(ProtoWriter {
108                bound: self.bound,
109                depth: self.depth + 1,
110            })
111        }
112    }
113
114    fn write_null(&mut self) -> Result<Self::Value, Self::Error> {
115        self.debit_value()?;
116        Ok(Kind::NullValue(0).into())
117    }
118
119    fn write_bool(&mut self, value: bool) -> Result<Self::Value, Self::Error> {
120        self.debit_value()?;
121        Ok(Kind::BoolValue(value).into())
122    }
123
124    fn write_number(&mut self, value: u32) -> Result<Self::Value, Self::Error> {
125        self.debit_value()?;
126        Ok(Value::from(value))
127    }
128
129    fn write_str(&mut self, value: String) -> Result<Self::Value, Self::Error> {
130        self.debit_string_value(&value)?;
131        Ok(Kind::StringValue(value).into())
132    }
133
134    fn write_vec(&mut self, value: Self::Vec) -> Result<Self::Value, Self::Error> {
135        self.debit_value()?;
136        Ok(Value::from(value))
137    }
138
139    fn write_map(&mut self, value: Self::Map) -> Result<Self::Value, Self::Error> {
140        self.debit_value()?;
141        Ok(Value::from(Kind::StructValue(value)))
142    }
143
144    fn vec_push_element(
145        &mut self,
146        vec: &mut Self::Vec,
147        val: Self::Value,
148    ) -> Result<(), Self::Error> {
149        vec.push(val);
150        Ok(())
151    }
152
153    fn map_push_field(
154        &mut self,
155        map: &mut Self::Map,
156        key: String,
157        val: Self::Value,
158    ) -> Result<(), Self::Error> {
159        self.debit_str(&key)?;
160        map.fields.insert(key, val);
161        Ok(())
162    }
163}
164
165impl From<RV::Error> for Error {
166    fn from(RV::Error: RV::Error) -> Self {
167        Error::UnexpectedType
168    }
169}
170
171impl From<OV::Error> for Error {
172    fn from(OV::Error: OV::Error) -> Self {
173        Error::UnexpectedType
174    }
175}
176
177#[cfg(test)]
178pub(crate) mod tests {
179    use super::*;
180
181    use crate::object::bounded_visitor::tests::layout_;
182    use crate::object::bounded_visitor::tests::serialize;
183    use crate::object::bounded_visitor::tests::value_;
184    use expect_test::expect;
185    use serde_json::json;
186
187    use A::MoveTypeLayout as L;
188    use A::MoveValue as V;
189
190    #[test]
191    fn test_simple() {
192        let type_layout = layout_(
193            "0x0::foo::Bar",
194            vec![
195                ("a", L::U64),
196                ("b", L::Vector(Box::new(L::U64))),
197                ("c", layout_("0x0::foo::Baz", vec![("d", L::U64)])),
198            ],
199        );
200
201        let value = value_(
202            "0x0::foo::Bar",
203            vec![
204                ("a", V::U64(42)),
205                ("b", V::Vector(vec![V::U64(43)])),
206                ("c", value_("0x0::foo::Baz", vec![("d", V::U64(44))])),
207            ],
208        );
209
210        let expected = json!({
211            "a": "42",
212            "b": ["43"],
213            "c": {
214                "d": "44"
215            }
216        });
217        let bound = required_budget(&expected);
218
219        let bytes = serialize(value.clone());
220
221        let deser = ProtoVisitor::new(bound)
222            .deserialize_value(&bytes, &type_layout)
223            .unwrap();
224
225        assert_eq!(expected, proto_value_to_json_value(deser));
226
227        ProtoVisitor::new(bound - 1)
228            .deserialize_value(&bytes, &type_layout)
229            .unwrap_err();
230    }
231
232    #[test]
233    fn test_too_deep() {
234        let mut layout = L::U64;
235        let mut value = V::U64(42);
236        let mut expected = serde_json::Value::from("42");
237
238        const DEPTH: usize = MAX_DEPTH;
239        for _ in 0..DEPTH {
240            layout = layout_("0x0::foo::Bar", vec![("f", layout)]);
241            value = value_("0x0::foo::Bar", vec![("f", value)]);
242            expected = json!({
243                "f": expected
244            });
245        }
246
247        let bound = required_budget(&expected);
248        let bytes = serialize(value.clone());
249
250        let deser = ProtoVisitor::new(bound)
251            .deserialize_value(&bytes, &layout)
252            .unwrap();
253
254        assert_eq!(expected, proto_value_to_json_value(deser));
255
256        // One deeper
257        layout = layout_("0x0::foo::Bar", vec![("f", layout)]);
258        value = value_("0x0::foo::Bar", vec![("f", value)]);
259
260        let bytes = serialize(value.clone());
261
262        let err = ProtoVisitor::new(bound)
263            .deserialize_value(&bytes, &layout)
264            .unwrap_err();
265
266        let expect = expect!["Exceeded maximum depth"];
267        expect.assert_eq(&err.to_string());
268    }
269
270    fn proto_value_to_json_value(proto: Value) -> serde_json::Value {
271        match proto.kind {
272            Some(Kind::NullValue(_)) | None => serde_json::Value::Null,
273            // Move doesn't support floats so for these tests can do a convert to u32
274            Some(Kind::NumberValue(n)) => serde_json::Value::from(n as u32),
275            Some(Kind::StringValue(s)) => serde_json::Value::from(s),
276            Some(Kind::BoolValue(b)) => serde_json::Value::from(b),
277            Some(Kind::StructValue(map)) => serde_json::Value::Object(
278                map.fields
279                    .into_iter()
280                    .map(|(k, v)| (k, proto_value_to_json_value(v)))
281                    .collect(),
282            ),
283            Some(Kind::ListValue(list_value)) => serde_json::Value::Array(
284                list_value
285                    .values
286                    .into_iter()
287                    .map(proto_value_to_json_value)
288                    .collect(),
289            ),
290        }
291    }
292
293    fn required_budget(json: &serde_json::Value) -> usize {
294        size_of::<Value>()
295            + match json {
296                serde_json::Value::Null => 0,
297                serde_json::Value::Bool(_) => 0,
298                serde_json::Value::Number(_) => 0,
299                serde_json::Value::String(s) => s.len(),
300                serde_json::Value::Array(vec) => vec.iter().map(required_budget).sum(),
301                serde_json::Value::Object(map) => {
302                    map.iter().map(|(k, v)| k.len() + required_budget(v)).sum()
303                }
304            }
305    }
306}