sui_types/
proto_value.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use crate::{
5    balance::Balance,
6    base_types::{RESOLVED_STD_OPTION, move_ascii_str_layout, move_utf8_str_layout, url_layout},
7    id::{ID, UID},
8    object::option_visitor as OV,
9};
10use move_core_types::{
11    account_address::AccountAddress, annotated_value as A, annotated_visitor as AV,
12    language_storage::TypeTag, u256::U256,
13};
14use prost_types::Struct;
15use prost_types::Value;
16use prost_types::value::Kind;
17
18/// This is the maximum depth of a proto message
19/// The maximum depth of a proto message is 100. Given this value may be nested itself somewhere
20/// we'll conservitively cap this to ~80% of that.
21const MAX_DEPTH: usize = 80;
22
23pub struct ProtoVisitorBuilder {
24    /// Budget to spend on visiting.
25    bound: usize,
26
27    /// Current level of nesting depth while visiting.
28    depth: usize,
29}
30
31struct ProtoVisitor<'a> {
32    /// Budget left to spend on visiting.
33    bound: &'a mut usize,
34
35    /// Current level of nesting depth while visiting.
36    depth: &'a mut usize,
37}
38
39#[derive(thiserror::Error, Debug)]
40pub enum Error {
41    #[error(transparent)]
42    Visitor(#[from] AV::Error),
43
44    #[error("Deserialized value too large")]
45    OutOfBudget,
46
47    #[error("Exceeded maximum depth")]
48    TooNested,
49
50    #[error("Unexpected type")]
51    UnexpectedType,
52}
53
54impl ProtoVisitorBuilder {
55    pub fn new(bound: usize) -> Self {
56        Self { bound, depth: 0 }
57    }
58
59    fn new_visitor(&mut self) -> Result<ProtoVisitor<'_>, Error> {
60        ProtoVisitor::new(&mut self.bound, &mut self.depth)
61    }
62
63    /// Deserialize `bytes` as a `MoveValue` with layout `layout`. Can fail if the bytes do not
64    /// represent a value with this layout, or if the deserialized value exceeds the field/type size
65    /// budget.
66    pub fn deserialize_value(
67        mut self,
68        bytes: &[u8],
69        layout: &A::MoveTypeLayout,
70    ) -> Result<Value, Error> {
71        let mut visitor = self.new_visitor()?;
72        A::MoveValue::visit_deserialize(bytes, layout, &mut visitor)
73    }
74}
75
76impl Drop for ProtoVisitor<'_> {
77    fn drop(&mut self) {
78        self.dec_depth();
79    }
80}
81
82impl<'a> ProtoVisitor<'a> {
83    fn new(bound: &'a mut usize, depth: &'a mut usize) -> Result<Self, Error> {
84        // Increment the depth since we're creating a new Visitor instance
85        Self::inc_depth(depth)?;
86        Ok(Self { bound, depth })
87    }
88
89    fn inc_depth(depth: &mut usize) -> Result<(), Error> {
90        if *depth > MAX_DEPTH {
91            Err(Error::TooNested)
92        } else {
93            *depth += 1;
94            Ok(())
95        }
96    }
97
98    fn dec_depth(&mut self) {
99        if *self.depth == 0 {
100            panic!("BUG: logic bug in Visitor implementation");
101        } else {
102            *self.depth -= 1;
103        }
104    }
105
106    /// Deduct `size` from the overall budget. Errors if `size` exceeds the current budget.
107    fn debit(&mut self, size: usize) -> Result<(), Error> {
108        if *self.bound < size {
109            Err(Error::OutOfBudget)
110        } else {
111            *self.bound -= size;
112            Ok(())
113        }
114    }
115
116    fn debit_value(&mut self) -> Result<(), Error> {
117        self.debit(size_of::<Value>())
118    }
119
120    fn debit_string_value(&mut self, s: &str) -> Result<(), Error> {
121        self.debit_str(s)?;
122        self.debit_value()
123    }
124
125    fn debit_str(&mut self, s: &str) -> Result<(), Error> {
126        self.debit(s.len())
127    }
128}
129
130impl<'b, 'l> AV::Visitor<'b, 'l> for ProtoVisitor<'_> {
131    type Value = Value;
132    type Error = Error;
133
134    fn visit_u8(&mut self, _: &AV::ValueDriver<'_, 'b, 'l>, value: u8) -> Result<Value, Error> {
135        self.debit_value()?;
136        Ok(Value::from(value))
137    }
138
139    fn visit_u16(&mut self, _: &AV::ValueDriver<'_, 'b, 'l>, value: u16) -> Result<Value, Error> {
140        self.debit_value()?;
141        Ok(Value::from(value))
142    }
143
144    fn visit_u32(&mut self, _: &AV::ValueDriver<'_, 'b, 'l>, value: u32) -> Result<Value, Error> {
145        self.debit_value()?;
146        Ok(Value::from(value))
147    }
148
149    fn visit_u64(&mut self, _: &AV::ValueDriver<'_, 'b, 'l>, value: u64) -> Result<Value, Error> {
150        let value = value.to_string();
151        self.debit_string_value(&value)?;
152        Ok(Value::from(value))
153    }
154
155    fn visit_u128(&mut self, _: &AV::ValueDriver<'_, 'b, 'l>, value: u128) -> Result<Value, Error> {
156        let value = value.to_string();
157        self.debit_string_value(&value)?;
158        Ok(Value::from(value))
159    }
160
161    fn visit_u256(&mut self, _: &AV::ValueDriver<'_, 'b, 'l>, value: U256) -> Result<Value, Error> {
162        let value = value.to_string();
163        self.debit_string_value(&value)?;
164        Ok(Value::from(value))
165    }
166
167    fn visit_bool(&mut self, _: &AV::ValueDriver<'_, 'b, 'l>, value: bool) -> Result<Value, Error> {
168        self.debit_value()?;
169        Ok(Value::from(value))
170    }
171
172    fn visit_address(
173        &mut self,
174        _: &AV::ValueDriver<'_, 'b, 'l>,
175        value: AccountAddress,
176    ) -> Result<Value, Error> {
177        let value = value.to_canonical_string(true);
178        self.debit_string_value(&value)?;
179        Ok(Value::from(value))
180    }
181
182    fn visit_signer(
183        &mut self,
184        _: &AV::ValueDriver<'_, 'b, 'l>,
185        value: AccountAddress,
186    ) -> Result<Value, Error> {
187        let value = value.to_canonical_string(true);
188        self.debit_string_value(&value)?;
189        Ok(Value::from(value))
190    }
191
192    fn visit_vector(&mut self, driver: &mut AV::VecDriver<'_, 'b, 'l>) -> Result<Value, Error> {
193        let value = if driver.element_layout().is_type(&TypeTag::U8) {
194            // Base64 encode arbitrary bytes
195            use base64::{Engine, engine::general_purpose::STANDARD};
196
197            if let Some(bytes) = driver
198                .bytes()
199                .get(driver.position()..(driver.position() + driver.len() as usize))
200            {
201                let b64 = STANDARD.encode(bytes);
202                self.debit_string_value(&b64)?;
203                Value::from(b64)
204            } else {
205                return Err(AV::Error::UnexpectedEof.into());
206            }
207        } else {
208            let mut elems = vec![];
209            self.debit_value()?;
210
211            while let Some(elem) =
212                driver.next_element(&mut ProtoVisitor::new(self.bound, self.depth)?)?
213            {
214                elems.push(elem);
215            }
216
217            Value::from(elems)
218        };
219
220        Ok(value)
221    }
222
223    fn visit_struct(&mut self, driver: &mut AV::StructDriver<'_, 'b, 'l>) -> Result<Value, Error> {
224        let ty = &driver.struct_layout().type_;
225        let layout = driver.struct_layout();
226
227        let value = if layout == &move_ascii_str_layout()
228            || layout == &move_utf8_str_layout()
229            || layout == &url_layout()
230        {
231            // 0x1::ascii::String or 0x1::string::String or 0x2::url::Url
232
233            let lo = driver.position();
234            driver.skip_field()?;
235            let hi = driver.position();
236
237            // HACK: Bypassing the layout to deserialize its bytes as a Rust type.
238            let bytes = &driver.bytes()[lo..hi];
239            let s: &str = bcs::from_bytes(bytes).map_err(|_| Error::UnexpectedType)?;
240            self.debit_string_value(s)?;
241            Value::from(s)
242        } else if layout == &UID::layout() || layout == &ID::layout() {
243            // 0x2::object::UID or 0x2::object::ID
244
245            let lo = driver.position();
246            driver.skip_field()?;
247            let hi = driver.position();
248
249            // HACK: Bypassing the layout to deserialize its bytes as a Rust type.
250            let bytes = &driver.bytes()[lo..hi];
251            let id = AccountAddress::from_bytes(bytes)
252                .map_err(|_| Error::UnexpectedType)?
253                .to_canonical_string(true);
254
255            self.debit_string_value(&id)?;
256            Value::from(id)
257        } else if (&ty.address, ty.module.as_ref(), ty.name.as_ref()) == RESOLVED_STD_OPTION {
258            // 0x1::option::Option
259            self.debit_value()?;
260            match OV::OptionVisitor(self).visit_struct(driver)? {
261                Some(value) => value,
262                None => Kind::NullValue(0).into(),
263            }
264        } else if Balance::is_balance_layout(layout) {
265            // 0x2::balance::Balance
266
267            let lo = driver.position();
268            driver.skip_field()?;
269            let hi = driver.position();
270
271            // HACK: Bypassing the layout to deserialize its bytes as a Rust type.
272            let bytes = &driver.bytes()[lo..hi];
273            let balance = bcs::from_bytes::<u64>(bytes)
274                .map_err(|_| Error::UnexpectedType)?
275                .to_string();
276            self.debit_string_value(&balance)?;
277            Value::from(balance)
278        } else {
279            // Arbitrary structs
280            let mut map = Struct::default();
281
282            self.debit_value()?;
283            for field in &driver.struct_layout().fields {
284                self.debit_str(field.name.as_str())?;
285            }
286
287            while let Some((field, elem)) =
288                driver.next_field(&mut ProtoVisitor::new(self.bound, self.depth)?)?
289            {
290                map.fields.insert(field.name.as_str().to_owned(), elem);
291            }
292            Value::from(Kind::StructValue(map))
293        };
294        Ok(value)
295    }
296
297    fn visit_variant(
298        &mut self,
299        driver: &mut AV::VariantDriver<'_, 'b, 'l>,
300    ) -> Result<Value, Error> {
301        let mut map = Struct::default();
302        self.debit_value()?;
303
304        self.debit_str("@variant")?;
305        self.debit_string_value(driver.variant_name().as_str())?;
306
307        map.fields
308            .insert("@variant".to_owned(), driver.variant_name().as_str().into());
309
310        for field in driver.variant_layout() {
311            self.debit_str(field.name.as_str())?;
312        }
313
314        while let Some((field, elem)) =
315            driver.next_field(&mut ProtoVisitor::new(self.bound, self.depth)?)?
316        {
317            map.fields.insert(field.name.as_str().to_owned(), elem);
318        }
319
320        Ok(Value::from(Kind::StructValue(map)))
321    }
322}
323
324impl From<OV::Error> for Error {
325    fn from(OV::Error: OV::Error) -> Self {
326        Error::UnexpectedType
327    }
328}
329
330#[cfg(test)]
331pub(crate) mod tests {
332    use std::str::FromStr;
333
334    use super::*;
335
336    use crate::object::bounded_visitor::tests::layout_;
337    use crate::object::bounded_visitor::tests::serialize;
338    use crate::object::bounded_visitor::tests::value_;
339    use expect_test::expect;
340    use move_core_types::annotated_value::MoveFieldLayout;
341    use move_core_types::annotated_value::MoveStructLayout;
342    use move_core_types::{ident_str, language_storage::StructTag};
343    use serde_json::json;
344
345    use A::MoveTypeLayout as L;
346    use A::MoveValue as V;
347
348    #[test]
349    fn test_simple() {
350        let type_layout = layout_(
351            "0x0::foo::Bar",
352            vec![
353                ("a", L::U64),
354                ("b", L::Vector(Box::new(L::U64))),
355                ("c", layout_("0x0::foo::Baz", vec![("d", L::U64)])),
356            ],
357        );
358
359        let value = value_(
360            "0x0::foo::Bar",
361            vec![
362                ("a", V::U64(42)),
363                ("b", V::Vector(vec![V::U64(43)])),
364                ("c", value_("0x0::foo::Baz", vec![("d", V::U64(44))])),
365            ],
366        );
367
368        let expected = json!({
369            "a": "42",
370            "b": ["43"],
371            "c": {
372                "d": "44"
373            }
374        });
375        let bound = required_budget(&expected);
376
377        let bytes = serialize(value.clone());
378
379        let deser = ProtoVisitorBuilder::new(bound)
380            .deserialize_value(&bytes, &type_layout)
381            .unwrap();
382
383        assert_eq!(expected, proto_value_to_json_value(deser));
384
385        ProtoVisitorBuilder::new(bound - 1)
386            .deserialize_value(&bytes, &type_layout)
387            .unwrap_err();
388    }
389
390    #[test]
391    fn test_too_deep() {
392        let mut layout = L::U64;
393        let mut value = V::U64(42);
394        let mut expected = serde_json::Value::from("42");
395
396        const DEPTH: usize = MAX_DEPTH;
397        for _ in 0..DEPTH {
398            layout = layout_("0x0::foo::Bar", vec![("f", layout)]);
399            value = value_("0x0::foo::Bar", vec![("f", value)]);
400            expected = json!({
401                "f": expected
402            });
403        }
404
405        let bound = required_budget(&expected);
406        let bytes = serialize(value.clone());
407
408        let deser = ProtoVisitorBuilder::new(bound)
409            .deserialize_value(&bytes, &layout)
410            .unwrap();
411
412        assert_eq!(expected, proto_value_to_json_value(deser));
413
414        // One deeper
415        layout = layout_("0x0::foo::Bar", vec![("f", layout)]);
416        value = value_("0x0::foo::Bar", vec![("f", value)]);
417
418        let bytes = serialize(value.clone());
419
420        let err = ProtoVisitorBuilder::new(bound)
421            .deserialize_value(&bytes, &layout)
422            .unwrap_err();
423
424        let expect = expect!["Exceeded maximum depth"];
425        expect.assert_eq(&err.to_string());
426    }
427
428    fn proto_value_to_json_value(proto: Value) -> serde_json::Value {
429        match proto.kind {
430            Some(Kind::NullValue(_)) | None => serde_json::Value::Null,
431            // Move doesn't support floats so for these tests can do a convert to u32
432            Some(Kind::NumberValue(n)) => serde_json::Value::from(n as u32),
433            Some(Kind::StringValue(s)) => serde_json::Value::from(s),
434            Some(Kind::BoolValue(b)) => serde_json::Value::from(b),
435            Some(Kind::StructValue(map)) => serde_json::Value::Object(
436                map.fields
437                    .into_iter()
438                    .map(|(k, v)| (k, proto_value_to_json_value(v)))
439                    .collect(),
440            ),
441            Some(Kind::ListValue(list_value)) => serde_json::Value::Array(
442                list_value
443                    .values
444                    .into_iter()
445                    .map(proto_value_to_json_value)
446                    .collect(),
447            ),
448        }
449    }
450
451    fn required_budget(json: &serde_json::Value) -> usize {
452        size_of::<Value>()
453            + match json {
454                serde_json::Value::Null => 0,
455                serde_json::Value::Bool(_) => 0,
456                serde_json::Value::Number(_) => 0,
457                serde_json::Value::String(s) => s.len(),
458                serde_json::Value::Array(vec) => vec.iter().map(required_budget).sum(),
459                serde_json::Value::Object(map) => {
460                    map.iter().map(|(k, v)| k.len() + required_budget(v)).sum()
461                }
462            }
463    }
464
465    //
466    // Tests for proper format rendering
467    //
468
469    fn json<T: serde::Serialize>(layout: A::MoveTypeLayout, data: T) -> serde_json::Value {
470        let bcs = bcs::to_bytes(&data).unwrap();
471        let proto_value = ProtoVisitorBuilder::new(1024 * 1024)
472            .deserialize_value(&bcs, &layout)
473            .unwrap();
474        proto_value_to_json_value(proto_value)
475    }
476
477    macro_rules! struct_layout {
478        ($type:literal { $($name:literal : $layout:expr),* $(,)?}) => {
479            A::MoveTypeLayout::Struct(Box::new(MoveStructLayout {
480                type_: StructTag::from_str($type).expect("Failed to parse struct"),
481                fields: vec![$(MoveFieldLayout {
482                    name: ident_str!($name).to_owned(),
483                    layout: $layout,
484                }),*]
485            }))
486        }
487    }
488
489    macro_rules! vector_layout {
490        ($inner:expr) => {
491            A::MoveTypeLayout::Vector(Box::new($inner))
492        };
493    }
494
495    fn address(a: &str) -> sui_sdk_types::Address {
496        sui_sdk_types::Address::from_str(a).unwrap()
497    }
498
499    #[test]
500    fn json_bool() {
501        let actual = json(L::Bool, true);
502        let expect = json!(true);
503        assert_eq!(expect, actual);
504
505        let actual = json(L::Bool, false);
506        let expect = json!(false);
507        assert_eq!(expect, actual);
508    }
509
510    #[test]
511    fn json_u8() {
512        let actual = json(L::U8, 42u8);
513        let expect = json!(42u8);
514        assert_eq!(expect, actual);
515    }
516
517    #[test]
518    fn json_u16() {
519        let actual = json(L::U16, 424u16);
520        let expect = json!(424u16);
521        assert_eq!(expect, actual);
522    }
523
524    #[test]
525    fn json_u32() {
526        let actual = json(L::U32, 432_432u32);
527        let expect = json!(432_432u32);
528        assert_eq!(expect, actual);
529    }
530
531    #[test]
532    fn json_u64() {
533        let actual = json(L::U64, 432_432_432_432u64);
534        let expect = json!(432_432_432_432u64.to_string());
535        assert_eq!(expect, actual);
536    }
537
538    #[test]
539    fn json_u128() {
540        let actual = json(L::U128, 424_242_424_242_424_242_424u128);
541        let expect = json!(424_242_424_242_424_242_424u128.to_string());
542        assert_eq!(expect, actual);
543    }
544
545    #[test]
546    fn json_u256() {
547        let actual = json(
548            L::U256,
549            U256::from_str("42424242424242424242424242424242424242424").unwrap(),
550        );
551        let expect = json!("42424242424242424242424242424242424242424");
552        assert_eq!(expect, actual);
553    }
554
555    #[test]
556    fn json_ascii_string() {
557        let l = struct_layout!("0x1::ascii::String" {
558            "bytes": vector_layout!(L::U8)
559        });
560        let actual = json(l, "The quick brown fox");
561        let expect = json!("The quick brown fox");
562        assert_eq!(expect, actual);
563    }
564
565    #[test]
566    fn json_utf8_string() {
567        let l = struct_layout!("0x1::string::String" {
568            "bytes": vector_layout!(L::U8)
569        });
570        let actual = json(l, "The quick brown fox");
571        let expect = json!("The quick brown fox");
572        assert_eq!(expect, actual);
573    }
574
575    #[test]
576    fn json_url() {
577        let l = struct_layout!("0x2::url::Url" {
578            "url": struct_layout!("0x1::ascii::String" {
579                "bytes": vector_layout!(L::U8)
580            })
581        });
582        let actual = json(l, "https://example.com");
583        let expect = json!("https://example.com");
584        assert_eq!(expect, actual);
585    }
586
587    #[test]
588    fn json_address() {
589        let actual = json(L::Address, address("0x42"));
590        let expect = json!(address("0x42").to_string());
591        assert_eq!(expect, actual);
592    }
593
594    #[test]
595    fn json_signer() {
596        let actual = json(L::Signer, address("0x42"));
597        let expect = json!(address("0x42").to_string());
598        assert_eq!(expect, actual);
599    }
600
601    #[test]
602    fn json_id() {
603        let l = struct_layout!("0x2::object::ID" {
604            "bytes": L::Address,
605        });
606        let actual = json(l, address("0x42"));
607        let expect = json!(address("0x42").to_string());
608        assert_eq!(expect, actual);
609    }
610
611    #[test]
612    fn json_uid() {
613        let l = struct_layout!("0x2::object::UID" {
614            "id": struct_layout!("0x2::object::ID" {
615                "bytes": L::Address,
616            })
617        });
618        let actual = json(l, address("0x42"));
619        let expect = json!(address("0x42").to_string());
620        assert_eq!(expect, actual);
621    }
622
623    #[test]
624    fn json_option() {
625        let l = struct_layout!("0x42::foo::Bar" {
626            "baz": struct_layout!("0x1::option::Option<u8>" { "vec": vector_layout!(L::U8) }),
627        });
628
629        let actual = json(l, Option::<Vec<u8>>::None);
630        let expect = json!({
631            "baz": null,
632        });
633        assert_eq!(expect, actual);
634    }
635
636    #[test]
637    fn json_balance() {
638        let l = struct_layout!("0x2::balance::Balance<0x2::sui::SUI>" {
639            "value": L::U64,
640        });
641
642        let actual = json(l, 100u64);
643        let expect = json!(100u64.to_string());
644        assert_eq!(expect, actual);
645    }
646
647    #[test]
648    fn json_compound() {
649        let l = struct_layout!("0x42::foo::Bar" {
650            "baz": struct_layout!("0x1::option::Option<u8>" { "vec": vector_layout!(L::U8) }),
651            "qux": vector_layout!(struct_layout!("0x43::xy::Zzy" {
652                "quy": L::U16,
653                "quz": struct_layout!("0x1::option::Option<0x1::ascii::String>" {
654                    "vec": vector_layout!(struct_layout!("0x1::ascii::String" {
655                        "bytes": vector_layout!(L::U8),
656                    }))
657                }),
658                "frob": L::Address,
659            })),
660        });
661
662        let actual = json(
663            l,
664            (
665                Option::<Vec<u8>>::None,
666                vec![
667                    (44u16, Some("Hello, world!"), address("0x45")),
668                    (46u16, None, address("0x47")),
669                ],
670            ),
671        );
672        let expect = json!({
673            "baz": null,
674            "qux": [{
675                "quy": 44,
676                "quz": "Hello, world!",
677                "frob": address("0x45").to_string(),
678            },
679            {
680                "quy": 46,
681                "quz": null,
682                "frob": address("0x47").to_string(),
683            }
684            ],
685        });
686        assert_eq!(expect, actual);
687    }
688}