sui_types/dynamic_field/
visitor.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use move_core_types::{
5    account_address::AccountAddress,
6    annotated_value as A,
7    annotated_visitor::{self, StructDriver, ValueDriver, VariantDriver, VecDriver, Visitor},
8    language_storage::TypeTag,
9    u256::U256,
10};
11
12use crate::{base_types::ObjectID, id::UID};
13
14use super::{DynamicFieldInfo, DynamicFieldType};
15
16/// Visitor to deserialize the outer structure of a `0x2::dynamic_field::Field` while leaving its
17/// name and value untouched.
18pub struct FieldVisitor;
19
20#[derive(Debug, Clone)]
21pub struct Field<'b, 'l> {
22    pub id: ObjectID,
23    pub kind: DynamicFieldType,
24    pub name_layout: &'l A::MoveTypeLayout,
25    pub name_bytes: &'b [u8],
26    pub value_layout: &'l A::MoveTypeLayout,
27    pub value_bytes: &'b [u8],
28}
29
30pub enum ValueMetadata {
31    DynamicField(TypeTag),
32    DynamicObjectField(ObjectID),
33}
34
35#[derive(thiserror::Error, Debug)]
36pub enum Error {
37    #[error("Not a dynamic field")]
38    NotADynamicField,
39
40    #[error("Not a dynamic object field")]
41    NotADynamicObjectField,
42
43    #[error("{0}")]
44    Visitor(#[from] annotated_visitor::Error),
45}
46
47impl FieldVisitor {
48    /// Deserialize the top-level structure from a dynamic field's `0x2::dynamic_field::Field`
49    /// without having to fully deserialize its name or value.
50    pub fn deserialize<'b, 'l>(
51        bytes: &'b [u8],
52        layout: &'l A::MoveTypeLayout,
53    ) -> Result<Field<'b, 'l>, Error> {
54        A::MoveValue::visit_deserialize(bytes, layout, &mut FieldVisitor)
55    }
56}
57
58impl Field<'_, '_> {
59    /// If this field is a dynamic field, returns its value's type. If it is a dynamic object
60    /// field, it returns the ID of the object the value points to (which must be fetched to
61    /// extract its type).
62    pub fn value_metadata(&self) -> Result<ValueMetadata, Error> {
63        match self.kind {
64            DynamicFieldType::DynamicField => Ok(ValueMetadata::DynamicField(TypeTag::from(
65                self.value_layout,
66            ))),
67
68            DynamicFieldType::DynamicObject => {
69                let id: ObjectID =
70                    bcs::from_bytes(self.value_bytes).map_err(|_| Error::NotADynamicObjectField)?;
71                Ok(ValueMetadata::DynamicObjectField(id))
72            }
73        }
74    }
75}
76
77impl<'b, 'l> Visitor<'b, 'l> for FieldVisitor {
78    type Value = Field<'b, 'l>;
79    type Error = Error;
80
81    fn visit_struct(
82        &mut self,
83        driver: &mut StructDriver<'_, 'b, 'l>,
84    ) -> Result<Self::Value, Error> {
85        if !DynamicFieldInfo::is_dynamic_field(&driver.struct_layout().type_) {
86            return Err(Error::NotADynamicField);
87        }
88
89        // Set-up optionals to fill while visiting fields -- all of them must be filled by the end
90        // to successfully return a `Field`.
91        let mut id = None;
92        let mut name_parts = None;
93        let mut value_parts = None;
94
95        while let Some(A::MoveFieldLayout { name, layout }) = driver.peek_field() {
96            match name.as_str() {
97                "id" => {
98                    let lo = driver.position();
99                    driver.skip_field()?;
100                    let hi = driver.position();
101
102                    if !matches!(layout, A::MoveTypeLayout::Struct(s) if s.as_ref() == &UID::layout())
103                    {
104                        return Err(Error::NotADynamicField);
105                    }
106
107                    // HACK: Bypassing `id`'s layout to deserialize its bytes as a Rust type.
108                    let bytes = &driver.bytes()[lo..hi];
109                    id = Some(ObjectID::from_bytes(bytes).map_err(|_| Error::NotADynamicField)?);
110                }
111
112                "name" => {
113                    let lo = driver.position();
114                    driver.skip_field()?;
115                    let hi = driver.position();
116
117                    let (kind, layout) = extract_name_layout(layout)?;
118                    name_parts = Some((&driver.bytes()[lo..hi], layout, kind));
119                }
120
121                "value" => {
122                    let lo = driver.position();
123                    driver.skip_field()?;
124                    let hi = driver.position();
125                    value_parts = Some((&driver.bytes()[lo..hi], layout));
126                }
127
128                _ => {
129                    return Err(Error::NotADynamicField);
130                }
131            }
132        }
133
134        let (Some(id), Some((name_bytes, name_layout, kind)), Some((value_bytes, value_layout))) =
135            (id, name_parts, value_parts)
136        else {
137            return Err(Error::NotADynamicField);
138        };
139
140        Ok(Field {
141            id,
142            kind,
143            name_layout,
144            name_bytes,
145            value_layout,
146            value_bytes,
147        })
148    }
149
150    // === Empty/default casees ===
151    //
152    // A dynamic field must be a struct, so if the visitor is fed anything else, it complains.
153
154    fn visit_u8(&mut self, _: &ValueDriver<'_, 'b, 'l>, _: u8) -> Result<Self::Value, Error> {
155        Err(Error::NotADynamicField)
156    }
157
158    fn visit_u16(&mut self, _: &ValueDriver<'_, 'b, 'l>, _: u16) -> Result<Self::Value, Error> {
159        Err(Error::NotADynamicField)
160    }
161
162    fn visit_u32(&mut self, _: &ValueDriver<'_, 'b, 'l>, _: u32) -> Result<Self::Value, Error> {
163        Err(Error::NotADynamicField)
164    }
165
166    fn visit_u64(&mut self, _: &ValueDriver<'_, 'b, 'l>, _: u64) -> Result<Self::Value, Error> {
167        Err(Error::NotADynamicField)
168    }
169
170    fn visit_u128(&mut self, _: &ValueDriver<'_, 'b, 'l>, _: u128) -> Result<Self::Value, Error> {
171        Err(Error::NotADynamicField)
172    }
173
174    fn visit_u256(&mut self, _: &ValueDriver<'_, 'b, 'l>, _: U256) -> Result<Self::Value, Error> {
175        Err(Error::NotADynamicField)
176    }
177
178    fn visit_bool(&mut self, _: &ValueDriver<'_, 'b, 'l>, _: bool) -> Result<Self::Value, Error> {
179        Err(Error::NotADynamicField)
180    }
181
182    fn visit_address(
183        &mut self,
184        _: &ValueDriver<'_, 'b, 'l>,
185        _: AccountAddress,
186    ) -> Result<Self::Value, Error> {
187        Err(Error::NotADynamicField)
188    }
189
190    fn visit_signer(
191        &mut self,
192        _: &ValueDriver<'_, 'b, 'l>,
193        _: AccountAddress,
194    ) -> Result<Self::Value, Error> {
195        Err(Error::NotADynamicField)
196    }
197
198    fn visit_vector(&mut self, _: &mut VecDriver<'_, 'b, 'l>) -> Result<Self::Value, Error> {
199        Err(Error::NotADynamicField)
200    }
201
202    fn visit_variant(&mut self, _: &mut VariantDriver<'_, 'b, 'l>) -> Result<Self::Value, Error> {
203        Err(Error::NotADynamicField)
204    }
205}
206
207/// Extract the type and layout of a dynamic field name, from the layout of its `Field.name`.
208fn extract_name_layout(
209    layout: &A::MoveTypeLayout,
210) -> Result<(DynamicFieldType, &A::MoveTypeLayout), Error> {
211    let A::MoveTypeLayout::Struct(struct_) = layout else {
212        return Ok((DynamicFieldType::DynamicField, layout));
213    };
214
215    if !DynamicFieldInfo::is_dynamic_object_field_wrapper(&struct_.type_) {
216        return Ok((DynamicFieldType::DynamicField, layout));
217    }
218
219    // Wrapper contains just one field
220    let [A::MoveFieldLayout { name, layout }] = &struct_.fields[..] else {
221        return Err(Error::NotADynamicField);
222    };
223
224    // ...called `name`
225    if name.as_str() != "name" {
226        return Err(Error::NotADynamicField);
227    }
228
229    Ok((DynamicFieldType::DynamicObject, layout))
230}
231
232#[cfg(test)]
233mod tests {
234    use std::str::FromStr;
235
236    use move_core_types::{
237        account_address::AccountAddress, annotated_value as A, language_storage::TypeTag,
238    };
239
240    use crate::{
241        base_types::ObjectID,
242        dynamic_field,
243        id::UID,
244        object::bounded_visitor::tests::{enum_, layout_, value_, variant_},
245    };
246
247    use super::*;
248
249    #[test]
250    fn test_dynamic_field_name() {
251        for (name, name_layout, name_bcs) in fixtures() {
252            for (value, value_layout, value_bcs) in fixtures() {
253                let df = serialized_df("0x264", name.clone(), value.clone());
254                let df_layout = df_layout(name_layout.clone(), value_layout.clone());
255                let field = FieldVisitor::deserialize(&df, &df_layout)
256                    .unwrap_or_else(|e| panic!("Failed to deserialize {name} => {value}: {e}"));
257
258                assert_eq!(field.id, oid_("0x264"), "{name} => {value}");
259                assert_eq!(field.name_bytes, &name_bcs, "{name} => {value}");
260                assert_eq!(field.value_bytes, &value_bcs, "{name} => {value}");
261
262                assert_eq!(
263                    field.kind,
264                    DynamicFieldType::DynamicField,
265                    "{name} => {value}",
266                );
267
268                assert_eq!(
269                    TypeTag::from(field.name_layout),
270                    TypeTag::from(&name_layout),
271                    "{name} => {value}",
272                );
273
274                assert_eq!(
275                    TypeTag::from(field.value_layout),
276                    TypeTag::from(&value_layout),
277                    "{name} => {value}",
278                );
279            }
280        }
281    }
282
283    #[test]
284    fn test_dynamic_object_field_name() {
285        let addr = A::MoveValue::Address(AccountAddress::ONE);
286        let id = value_("0x2::object::ID", vec![("bytes", addr)]);
287        let id_bcs = id.clone().undecorate().simple_serialize().unwrap();
288
289        for (name, name_layout, name_bcs) in fixtures() {
290            let df = serialized_df("0x264", name.clone(), id.clone());
291            let df_layout = dof_layout(name_layout.clone());
292            let field = FieldVisitor::deserialize(&df, &df_layout)
293                .unwrap_or_else(|e| panic!("Failed to deserialize {name}: {e}"));
294
295            assert_eq!(field.id, oid_("0x264"), "{name}");
296            assert_eq!(field.name_bytes, &name_bcs, "{name}");
297            assert_eq!(field.value_bytes, &id_bcs, "{name}");
298
299            assert_eq!(field.kind, DynamicFieldType::DynamicObject, "{name}",);
300
301            assert_eq!(
302                TypeTag::from(field.name_layout),
303                TypeTag::from(&name_layout),
304                "{name}",
305            );
306
307            assert_eq!(
308                TypeTag::from(field.value_layout),
309                TypeTag::from(&id_layout()),
310                "{name}",
311            );
312        }
313    }
314
315    #[test]
316    fn test_name_from_not_dynamic_field() {
317        for (value, layout, bytes) in fixtures() {
318            let Err(e) = FieldVisitor::deserialize(&bytes, &layout) else {
319                panic!("Expected NotADynamicField error for {value}");
320            };
321
322            assert_eq!(
323                e.to_string(),
324                "Not a dynamic field",
325                "Unexpected error for {value}"
326            );
327        }
328    }
329
330    /// If the visitor is run over a type that isn't actually a `0x2::dynamic_field::Field`, it
331    /// will complain.
332    #[test]
333    fn test_from_bad_type() {
334        for (value, layout, bytes) in fixtures() {
335            let Err(e) = FieldVisitor::deserialize(&bytes, &layout) else {
336                panic!("Expected NotADynamicField error for {value}");
337            };
338
339            assert_eq!(
340                e.to_string(),
341                "Not a dynamic field",
342                "Unexpected error for {value}"
343            );
344        }
345    }
346
347    #[test]
348    fn test_from_dynamic_field_missing_id() {
349        let bytes = bcs::to_bytes(&(42u8, 43u8)).unwrap();
350        let layout = layout_(
351            "0x2::dynamic_field::Field<u8, u8>",
352            vec![
353                ("name", A::MoveTypeLayout::U8),
354                ("value", A::MoveTypeLayout::U8),
355            ],
356        );
357
358        let Err(e) = FieldVisitor::deserialize(&bytes, &layout) else {
359            panic!("Expected NotADynamicField error");
360        };
361
362        assert_eq!(e.to_string(), "Not a dynamic field");
363    }
364
365    #[test]
366    fn test_from_dynamic_field_missing_name() {
367        let bytes = bcs::to_bytes(&(oid_("0x264"), 43u8)).unwrap();
368        let layout = layout_(
369            "0x2::dynamic_field::Field<u8, u8>",
370            vec![("id", id_layout()), ("value", A::MoveTypeLayout::U8)],
371        );
372
373        let Err(e) = FieldVisitor::deserialize(&bytes, &layout) else {
374            panic!("Expected NotADynamicField error");
375        };
376
377        assert_eq!(e.to_string(), "Not a dynamic field");
378    }
379
380    #[test]
381    fn test_from_dynamic_field_missing_value() {
382        let bytes = bcs::to_bytes(&(oid_("0x264"), 42u8)).unwrap();
383        let layout = layout_(
384            "0x2::dynamic_field::Field<u8, u8>",
385            vec![("id", id_layout()), ("name", A::MoveTypeLayout::U8)],
386        );
387
388        let Err(e) = FieldVisitor::deserialize(&bytes, &layout) else {
389            panic!("Expected NotADynamicField error");
390        };
391
392        assert_eq!(e.to_string(), "Not a dynamic field");
393    }
394
395    #[test]
396    fn test_from_dynamic_field_weird_id() {
397        let bytes = bcs::to_bytes(&(42u8, 43u8, 44u8)).unwrap();
398        let layout = layout_(
399            "0x2::dynamic_field::Field<u8, u8>",
400            vec![
401                ("id", A::MoveTypeLayout::U8),
402                ("name", A::MoveTypeLayout::U8),
403                ("value", A::MoveTypeLayout::U8),
404            ],
405        );
406
407        let Err(e) = FieldVisitor::deserialize(&bytes, &layout) else {
408            panic!("Expected NotADynamicField error");
409        };
410
411        assert_eq!(e.to_string(), "Not a dynamic field");
412    }
413
414    /// If the name is wrapped in `0x2::dynamic_object_field::Wrapper`, but the wrapper's structure
415    /// is somehow incorrect, that will result in an error.
416    #[test]
417    fn test_from_dynamic_object_field_bad_wrapper() {
418        let bytes = bcs::to_bytes(&(oid_("0x264"), 42u8)).unwrap();
419        let layout = layout_(
420            "0x2::dynamic_field::Field<0x2::dynamic_object_field::Wrapper<u8>, u8>",
421            vec![
422                ("id", id_layout()),
423                (
424                    "name",
425                    layout_(
426                        "0x2::dynamic_object_field::Wrapper<u8>",
427                        // In the real type, the field is called "name"
428                        vec![("wrapped", A::MoveTypeLayout::U8)],
429                    ),
430                ),
431                ("value", A::MoveTypeLayout::U8),
432            ],
433        );
434
435        let Err(e) = FieldVisitor::deserialize(&bytes, &layout) else {
436            panic!("Expected NotADynamicField error");
437        };
438
439        assert_eq!(e.to_string(), "Not a dynamic field");
440    }
441
442    /// Various Move values to use as dynamic field names and values.
443    fn fixtures() -> Vec<(A::MoveValue, A::MoveTypeLayout, Vec<u8>)> {
444        use A::MoveTypeLayout as T;
445        use A::MoveValue as V;
446
447        vec![
448            fixture(V::U8(42), T::U8),
449            fixture(V::Address(AccountAddress::ONE), T::Address),
450            fixture(
451                V::Vector(vec![V::U32(43), V::U32(44), V::U32(45)]),
452                T::Vector(Box::new(T::U32)),
453            ),
454            fixture(
455                value_(
456                    "0x2::object::ID",
457                    vec![("bytes", V::Address(AccountAddress::TWO))],
458                ),
459                layout_("0x2::object::ID", vec![("bytes", T::Address)]),
460            ),
461            fixture(
462                variant_(
463                    "0x1::option::Option<u64>",
464                    "Some",
465                    1,
466                    vec![("value", V::U64(46))],
467                ),
468                enum_(
469                    "0x1::option::Option<u64>",
470                    vec![
471                        (("None", 0), vec![]),
472                        (("Some", 1), vec![("value", T::U64)]),
473                    ],
474                ),
475            ),
476        ]
477    }
478
479    fn fixture(
480        value: A::MoveValue,
481        layout: A::MoveTypeLayout,
482    ) -> (A::MoveValue, A::MoveTypeLayout, Vec<u8>) {
483        let bytes = value
484            .clone()
485            .undecorate()
486            .simple_serialize()
487            .unwrap_or_else(|| panic!("Failed to serialize {}", value.clone()));
488
489        (value, layout, bytes)
490    }
491
492    fn oid_(rep: &str) -> ObjectID {
493        ObjectID::from_str(rep).unwrap()
494    }
495
496    fn serialized_df(id: &str, name: A::MoveValue, value: A::MoveValue) -> Vec<u8> {
497        bcs::to_bytes(&dynamic_field::Field {
498            id: UID::new(oid_(id)),
499            name: name.undecorate(),
500            value: value.undecorate(),
501        })
502        .unwrap()
503    }
504
505    fn id_layout() -> A::MoveTypeLayout {
506        let addr = A::MoveTypeLayout::Address;
507        layout_("0x2::object::ID", vec![("bytes", addr)])
508    }
509
510    fn df_layout(name: A::MoveTypeLayout, value: A::MoveTypeLayout) -> A::MoveTypeLayout {
511        let uid = layout_("0x2::object::UID", vec![("id", id_layout())]);
512        let field = format!(
513            "0x2::dynamic_field::Field<{}, {}>",
514            TypeTag::from(&name).to_canonical_display(/* with_prefix */ true),
515            TypeTag::from(&value).to_canonical_display(/* with_prefix */ true)
516        );
517
518        layout_(&field, vec![("id", uid), ("name", name), ("value", value)])
519    }
520
521    fn dof_layout(name: A::MoveTypeLayout) -> A::MoveTypeLayout {
522        let tag = TypeTag::from(&name);
523        let wrapper = format!(
524            "0x2::dynamic_object_field::Wrapper<{}>",
525            tag.to_canonical_display(/* with_prefix */ true)
526        );
527
528        let name = layout_(&wrapper, vec![("name", name)]);
529        df_layout(name, id_layout())
530    }
531}