sui_move_natives_v2/object_runtime/
mod.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4pub(crate) mod object_store;
5
6use self::object_store::{ChildObjectEffect, ObjectResult};
7use super::get_object_id;
8use better_any::{Tid, TidAble};
9use indexmap::map::IndexMap;
10use indexmap::set::IndexSet;
11use move_binary_format::errors::{PartialVMError, PartialVMResult};
12use move_core_types::{
13    account_address::AccountAddress,
14    annotated_value::{MoveTypeLayout, MoveValue},
15    annotated_visitor as AV,
16    language_storage::StructTag,
17    runtime_value as R,
18    vm_status::StatusCode,
19};
20use move_vm_types::{
21    effects::Op,
22    loaded_data::runtime_types::Type,
23    values::{GlobalValue, Value},
24};
25use object_store::ChildObjectStore;
26use std::{
27    collections::{BTreeMap, BTreeSet},
28    sync::Arc,
29};
30use sui_protocol_config::{check_limit_by_meter, LimitThresholdCrossed, ProtocolConfig};
31use sui_types::{
32    base_types::{MoveObjectType, ObjectID, SequenceNumber, SuiAddress},
33    committee::EpochId,
34    error::{ExecutionError, VMMemoryLimitExceededSubStatusCode},
35    execution::DynamicallyLoadedObjectMetadata,
36    execution_status::ExecutionErrorKind,
37    id::UID,
38    metrics::ExecutionMetrics,
39    object::{MoveObject, Owner},
40    storage::ChildObjectResolver,
41    SUI_AUTHENTICATOR_STATE_OBJECT_ID, SUI_CLOCK_OBJECT_ID, SUI_DENY_LIST_OBJECT_ID,
42    SUI_RANDOMNESS_STATE_OBJECT_ID, SUI_SYSTEM_STATE_OBJECT_ID,
43};
44
45pub enum ObjectEvent {
46    /// Transfer to a new address or object. Or make it shared or immutable.
47    Transfer(Owner, MoveObject),
48    /// An object ID is deleted
49    DeleteObjectID(ObjectID),
50}
51
52type Set<K> = IndexSet<K>;
53
54#[derive(Default)]
55pub(crate) struct TestInventories {
56    pub(crate) objects: BTreeMap<ObjectID, Value>,
57    // address inventories. Most recent objects are at the back of the set
58    pub(crate) address_inventories: BTreeMap<SuiAddress, BTreeMap<Type, Set<ObjectID>>>,
59    // global inventories.Most recent objects are at the back of the set
60    pub(crate) shared_inventory: BTreeMap<Type, Set<ObjectID>>,
61    pub(crate) immutable_inventory: BTreeMap<Type, Set<ObjectID>>,
62    pub(crate) taken_immutable_values: BTreeMap<Type, BTreeMap<ObjectID, Value>>,
63    // object has been taken from the inventory
64    pub(crate) taken: BTreeMap<ObjectID, Owner>,
65}
66
67pub struct LoadedRuntimeObject {
68    pub version: SequenceNumber,
69    pub is_modified: bool,
70}
71
72pub struct RuntimeResults {
73    pub writes: IndexMap<ObjectID, (Owner, Type, Value)>,
74    pub user_events: Vec<(Type, StructTag, Value)>,
75    // Loaded child objects, their loaded version/digest and whether they were modified.
76    pub loaded_child_objects: BTreeMap<ObjectID, LoadedRuntimeObject>,
77    pub created_object_ids: Set<ObjectID>,
78    pub deleted_object_ids: Set<ObjectID>,
79}
80
81#[derive(Default)]
82pub(crate) struct ObjectRuntimeState {
83    pub(crate) input_objects: BTreeMap<ObjectID, Owner>,
84    // new ids from object::new
85    new_ids: Set<ObjectID>,
86    // ids passed to object::delete
87    deleted_ids: Set<ObjectID>,
88    // transfers to a new owner (shared, immutable, object, or account address)
89    // TODO these struct tags can be removed if type_to_type_tag was exposed in the session
90    transfers: IndexMap<ObjectID, (Owner, Type, Value)>,
91    events: Vec<(Type, StructTag, Value)>,
92    // total size of events emitted so far
93    total_events_size: u64,
94    received: IndexMap<ObjectID, DynamicallyLoadedObjectMetadata>,
95}
96
97#[derive(Tid)]
98pub struct ObjectRuntime<'a> {
99    child_object_store: ChildObjectStore<'a>,
100    // inventories for test scenario
101    pub(crate) test_inventories: TestInventories,
102    // the internal state
103    pub(crate) state: ObjectRuntimeState,
104    // whether or not this TX is gas metered
105    is_metered: bool,
106
107    pub(crate) protocol_config: &'a ProtocolConfig,
108    pub(crate) metrics: Arc<ExecutionMetrics>,
109}
110
111pub enum TransferResult {
112    New,
113    SameOwner,
114    OwnerChanged,
115}
116
117pub struct InputObject {
118    pub contained_uids: BTreeSet<ObjectID>,
119    pub version: SequenceNumber,
120    pub owner: Owner,
121}
122
123impl TestInventories {
124    fn new() -> Self {
125        Self::default()
126    }
127}
128
129impl<'a> ObjectRuntime<'a> {
130    pub fn new(
131        object_resolver: &'a dyn ChildObjectResolver,
132        input_objects: BTreeMap<ObjectID, InputObject>,
133        is_metered: bool,
134        protocol_config: &'a ProtocolConfig,
135        metrics: Arc<ExecutionMetrics>,
136        epoch_id: EpochId,
137    ) -> Self {
138        let mut input_object_owners = BTreeMap::new();
139        let mut root_version = BTreeMap::new();
140        let mut wrapped_object_containers = BTreeMap::new();
141        for (id, input_object) in input_objects {
142            let InputObject {
143                contained_uids,
144                version,
145                owner,
146            } = input_object;
147            input_object_owners.insert(id, owner);
148            debug_assert!(contained_uids.contains(&id));
149            for contained_uid in contained_uids {
150                root_version.insert(contained_uid, version);
151                if contained_uid != id {
152                    let prev = wrapped_object_containers.insert(contained_uid, id);
153                    debug_assert!(prev.is_none());
154                }
155            }
156        }
157        Self {
158            child_object_store: ChildObjectStore::new(
159                object_resolver,
160                root_version,
161                wrapped_object_containers,
162                is_metered,
163                protocol_config,
164                metrics.clone(),
165                epoch_id,
166            ),
167            test_inventories: TestInventories::new(),
168            state: ObjectRuntimeState {
169                input_objects: input_object_owners,
170                new_ids: Set::new(),
171                deleted_ids: Set::new(),
172                transfers: IndexMap::new(),
173                events: vec![],
174                total_events_size: 0,
175                received: IndexMap::new(),
176            },
177            is_metered,
178            protocol_config,
179            metrics,
180        }
181    }
182
183    pub fn new_id(&mut self, id: ObjectID) -> PartialVMResult<()> {
184        // If metered, we use the metered limit (non system tx limit) as the hard limit
185        // This macro takes care of that
186        if let LimitThresholdCrossed::Hard(_, lim) = check_limit_by_meter!(
187            self.is_metered,
188            self.state.new_ids.len(),
189            self.protocol_config.max_num_new_move_object_ids(),
190            self.protocol_config.max_num_new_move_object_ids_system_tx(),
191            self.metrics.limits_metrics.excessive_new_move_object_ids
192        ) {
193            return Err(PartialVMError::new(StatusCode::MEMORY_LIMIT_EXCEEDED)
194                .with_message(format!("Creating more than {} IDs is not allowed", lim))
195                .with_sub_status(
196                    VMMemoryLimitExceededSubStatusCode::NEW_ID_COUNT_LIMIT_EXCEEDED as u64,
197                ));
198        };
199
200        // remove from deleted_ids for the case in dynamic fields where the Field object was deleted
201        // and then re-added in a single transaction. In that case, we also skip adding it
202        // to new_ids.
203        let was_present = self.state.deleted_ids.shift_remove(&id);
204        if !was_present {
205            // mark the id as new
206            self.state.new_ids.insert(id);
207        }
208        Ok(())
209    }
210
211    pub fn delete_id(&mut self, id: ObjectID) -> PartialVMResult<()> {
212        // This is defensive because `self.state.deleted_ids` may not indeed
213        // be called based on the `was_new` flag
214        // Metered transactions don't have limits for now
215
216        if let LimitThresholdCrossed::Hard(_, lim) = check_limit_by_meter!(
217            self.is_metered,
218            self.state.deleted_ids.len(),
219            self.protocol_config.max_num_deleted_move_object_ids(),
220            self.protocol_config
221                .max_num_deleted_move_object_ids_system_tx(),
222            self.metrics
223                .limits_metrics
224                .excessive_deleted_move_object_ids
225        ) {
226            return Err(PartialVMError::new(StatusCode::MEMORY_LIMIT_EXCEEDED)
227                .with_message(format!("Deleting more than {} IDs is not allowed", lim))
228                .with_sub_status(
229                    VMMemoryLimitExceededSubStatusCode::DELETED_ID_COUNT_LIMIT_EXCEEDED as u64,
230                ));
231        };
232
233        let was_new = self.state.new_ids.shift_remove(&id);
234        if !was_new {
235            self.state.deleted_ids.insert(id);
236        }
237        Ok(())
238    }
239
240    pub fn transfer(
241        &mut self,
242        owner: Owner,
243        ty: Type,
244        obj: Value,
245    ) -> PartialVMResult<TransferResult> {
246        let id: ObjectID = get_object_id(obj.copy_value()?)?
247            .value_as::<AccountAddress>()?
248            .into();
249        // - An object is new if it is contained in the new ids or if it is one of the objects
250        //   created during genesis (the system state object or clock).
251        // - Otherwise, check the input objects for the previous owner
252        // - If it was not in the input objects, it must have been wrapped or must have been a
253        //   child object
254        let is_framework_obj = [
255            SUI_SYSTEM_STATE_OBJECT_ID,
256            SUI_CLOCK_OBJECT_ID,
257            SUI_AUTHENTICATOR_STATE_OBJECT_ID,
258            SUI_RANDOMNESS_STATE_OBJECT_ID,
259            SUI_DENY_LIST_OBJECT_ID,
260        ]
261        .contains(&id);
262        let transfer_result = if self.state.new_ids.contains(&id) {
263            TransferResult::New
264        } else if is_framework_obj {
265            // framework objects are always created when they are transferred, but the id is
266            // hard-coded so it is not yet in new_ids
267            self.state.new_ids.insert(id);
268            TransferResult::New
269        } else if let Some(prev_owner) = self.state.input_objects.get(&id) {
270            match (&owner, prev_owner) {
271                // don't use == for dummy values in Shared owner
272                (Owner::Shared { .. }, Owner::Shared { .. }) => TransferResult::SameOwner,
273                (new, old) if new == old => TransferResult::SameOwner,
274                _ => TransferResult::OwnerChanged,
275            }
276        } else {
277            TransferResult::OwnerChanged
278        };
279
280        // Metered transactions don't have limits for now
281
282        if let LimitThresholdCrossed::Hard(_, lim) = check_limit_by_meter!(
283            // TODO: is this not redundant? Metered TX implies framework obj cannot be transferred
284            self.is_metered && !is_framework_obj, // We have higher limits for unmetered transactions and framework obj
285            self.state.transfers.len(),
286            self.protocol_config.max_num_transferred_move_object_ids(),
287            self.protocol_config
288                .max_num_transferred_move_object_ids_system_tx(),
289            self.metrics
290                .limits_metrics
291                .excessive_transferred_move_object_ids
292        ) {
293            return Err(PartialVMError::new(StatusCode::MEMORY_LIMIT_EXCEEDED)
294                .with_message(format!("Transferring more than {} IDs is not allowed", lim))
295                .with_sub_status(
296                    VMMemoryLimitExceededSubStatusCode::TRANSFER_ID_COUNT_LIMIT_EXCEEDED as u64,
297                ));
298        };
299
300        self.state.transfers.insert(id, (owner, ty, obj));
301        Ok(transfer_result)
302    }
303
304    pub fn emit_event(&mut self, ty: Type, tag: StructTag, event: Value) -> PartialVMResult<()> {
305        if self.state.events.len() >= (self.protocol_config.max_num_event_emit() as usize) {
306            return Err(max_event_error(self.protocol_config.max_num_event_emit()));
307        }
308        self.state.events.push((ty, tag, event));
309        Ok(())
310    }
311
312    pub fn take_user_events(&mut self) -> Vec<(Type, StructTag, Value)> {
313        std::mem::take(&mut self.state.events)
314    }
315
316    pub(crate) fn child_object_exists(
317        &mut self,
318        parent: ObjectID,
319        child: ObjectID,
320    ) -> PartialVMResult<bool> {
321        self.child_object_store.object_exists(parent, child)
322    }
323
324    pub(crate) fn child_object_exists_and_has_type(
325        &mut self,
326        parent: ObjectID,
327        child: ObjectID,
328        child_type: &MoveObjectType,
329    ) -> PartialVMResult<bool> {
330        self.child_object_store
331            .object_exists_and_has_type(parent, child, child_type)
332    }
333
334    pub(super) fn receive_object(
335        &mut self,
336        parent: ObjectID,
337        child: ObjectID,
338        child_version: SequenceNumber,
339        child_ty: &Type,
340        child_layout: &R::MoveTypeLayout,
341        child_fully_annotated_layout: &MoveTypeLayout,
342        child_move_type: MoveObjectType,
343    ) -> PartialVMResult<Option<ObjectResult<Value>>> {
344        let Some((value, obj_meta)) = self.child_object_store.receive_object(
345            parent,
346            child,
347            child_version,
348            child_ty,
349            child_layout,
350            child_fully_annotated_layout,
351            child_move_type,
352        )?
353        else {
354            return Ok(None);
355        };
356        // NB: It is important that the object only be added to the received set after it has been
357        // fully authenticated and loaded.
358        if self.state.received.insert(child, obj_meta).is_some() {
359            // We should never hit this -- it means that we have received the same object twice which
360            // means we have a duplicated a receiving ticket somehow.
361            return Err(
362                PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR).with_message(format!(
363                    "Object {child} at version {child_version} already received. This can only happen \
364                    if multiple `Receiving` arguments exist for the same object in the transaction which is impossible."
365                )),
366            );
367        }
368        Ok(Some(value))
369    }
370
371    pub(crate) fn get_or_fetch_child_object(
372        &mut self,
373        parent: ObjectID,
374        child: ObjectID,
375        child_ty: &Type,
376        child_layout: &R::MoveTypeLayout,
377        child_fully_annotated_layout: &MoveTypeLayout,
378        child_move_type: MoveObjectType,
379    ) -> PartialVMResult<ObjectResult<&mut GlobalValue>> {
380        let res = self.child_object_store.get_or_fetch_object(
381            parent,
382            child,
383            child_ty,
384            child_layout,
385            child_fully_annotated_layout,
386            child_move_type,
387        )?;
388        Ok(match res {
389            ObjectResult::MismatchedType => ObjectResult::MismatchedType,
390            ObjectResult::Loaded(child_object) => ObjectResult::Loaded(&mut child_object.value),
391        })
392    }
393
394    pub(crate) fn add_child_object(
395        &mut self,
396        parent: ObjectID,
397        child: ObjectID,
398        child_ty: &Type,
399        child_move_type: MoveObjectType,
400        child_value: Value,
401    ) -> PartialVMResult<()> {
402        self.child_object_store
403            .add_object(parent, child, child_ty, child_move_type, child_value)
404    }
405
406    // returns None if a child object is still borrowed
407    pub(crate) fn take_state(&mut self) -> ObjectRuntimeState {
408        std::mem::take(&mut self.state)
409    }
410
411    pub fn finish(mut self) -> Result<RuntimeResults, ExecutionError> {
412        let loaded_child_objects = self.loaded_runtime_objects();
413        let child_effects = self.child_object_store.take_effects();
414        self.state.finish(loaded_child_objects, child_effects)
415    }
416
417    pub(crate) fn all_active_child_objects(
418        &self,
419    ) -> impl Iterator<Item = (&ObjectID, &Type, Value)> {
420        self.child_object_store.all_active_objects()
421    }
422
423    pub fn loaded_runtime_objects(&self) -> BTreeMap<ObjectID, DynamicallyLoadedObjectMetadata> {
424        // The loaded child objects, and the received objects, should be disjoint. If they are not,
425        // this is an error since it could lead to incorrect transaction dependency computations.
426        debug_assert!(self
427            .child_object_store
428            .cached_objects()
429            .keys()
430            .all(|id| !self.state.received.contains_key(id)));
431        self.child_object_store
432            .cached_objects()
433            .iter()
434            .filter_map(|(id, obj_opt)| {
435                obj_opt.as_ref().map(|obj| {
436                    (
437                        *id,
438                        DynamicallyLoadedObjectMetadata {
439                            version: obj.version(),
440                            digest: obj.digest(),
441                            storage_rebate: obj.storage_rebate,
442                            owner: obj.owner.clone(),
443                            previous_transaction: obj.previous_transaction,
444                        },
445                    )
446                })
447            })
448            .chain(
449                self.state
450                    .received
451                    .iter()
452                    .map(|(id, meta)| (*id, meta.clone())),
453            )
454            .collect()
455    }
456
457    /// A map from wrapped objects to the object that wraps them at the beginning of the
458    /// transaction.
459    pub fn wrapped_object_containers(&self) -> BTreeMap<ObjectID, ObjectID> {
460        self.child_object_store.wrapped_object_containers().clone()
461    }
462}
463
464pub fn max_event_error(max_events: u64) -> PartialVMError {
465    PartialVMError::new(StatusCode::MEMORY_LIMIT_EXCEEDED)
466        .with_message(format!(
467            "Emitting more than {} events is not allowed",
468            max_events
469        ))
470        .with_sub_status(VMMemoryLimitExceededSubStatusCode::EVENT_COUNT_LIMIT_EXCEEDED as u64)
471}
472
473impl ObjectRuntimeState {
474    /// Update `state_view` with the effects of successfully executing a transaction:
475    /// - Given the effects `Op<Value>` of child objects, processes the changes in terms of
476    ///   object writes/deletes
477    /// - Process `transfers` and `input_objects` to determine whether the type of change
478    ///   (WriteKind) to the object
479    /// - Process `deleted_ids` with previously determined information to determine the
480    ///   DeleteKind
481    /// - Passes through user events
482    pub(crate) fn finish(
483        mut self,
484        loaded_child_objects: BTreeMap<ObjectID, DynamicallyLoadedObjectMetadata>,
485        child_object_effects: BTreeMap<ObjectID, ChildObjectEffect>,
486    ) -> Result<RuntimeResults, ExecutionError> {
487        let mut loaded_child_objects: BTreeMap<_, _> = loaded_child_objects
488            .into_iter()
489            .map(|(id, metadata)| {
490                (
491                    id,
492                    LoadedRuntimeObject {
493                        version: metadata.version,
494                        is_modified: false,
495                    },
496                )
497            })
498            .collect();
499        for (child, child_object_effect) in child_object_effects {
500            let ChildObjectEffect {
501                owner: parent,
502                ty,
503                effect,
504            } = child_object_effect;
505
506            if let Some(loaded_child) = loaded_child_objects.get_mut(&child) {
507                loaded_child.is_modified = true;
508            }
509
510            match effect {
511                // was modified, so mark it as mutated and transferred
512                Op::Modify(v) => {
513                    debug_assert!(!self.transfers.contains_key(&child));
514                    debug_assert!(!self.new_ids.contains(&child));
515                    debug_assert!(loaded_child_objects.contains_key(&child));
516                    self.transfers
517                        .insert(child, (Owner::ObjectOwner(parent.into()), ty, v));
518                }
519
520                Op::New(v) => {
521                    debug_assert!(!self.transfers.contains_key(&child));
522                    self.transfers
523                        .insert(child, (Owner::ObjectOwner(parent.into()), ty, v));
524                }
525
526                Op::Delete => {
527                    // was transferred so not actually deleted
528                    if self.transfers.contains_key(&child) {
529                        debug_assert!(!self.deleted_ids.contains(&child));
530                    }
531                    // ID was deleted too was deleted so mark as deleted
532                    if self.deleted_ids.contains(&child) {
533                        debug_assert!(!self.transfers.contains_key(&child));
534                        debug_assert!(!self.new_ids.contains(&child));
535                    }
536                }
537            }
538        }
539        let ObjectRuntimeState {
540            input_objects: _,
541            new_ids,
542            deleted_ids,
543            transfers,
544            events: user_events,
545            total_events_size: _,
546            received,
547        } = self;
548
549        // Check new owners from transfers, reports an error on cycles.
550        // TODO can we have cycles in the new system?
551        check_circular_ownership(
552            transfers
553                .iter()
554                .map(|(id, (owner, _, _))| (*id, owner.clone())),
555        )?;
556        // For both written_objects and deleted_ids, we need to mark the loaded child object as modified.
557        // These may not be covered in the child object effects if they are taken out in one PT command and then
558        // transferred/deleted in a different command. Marking them as modified will allow us properly determine their
559        // mutation category in effects.
560        // TODO: This could get error-prone quickly: what if we forgot to mark an object as modified? There may be a cleaner
561        // sulution.
562        let written_objects: IndexMap<_, _> = transfers
563            .into_iter()
564            .map(|(id, (owner, type_, value))| {
565                if let Some(loaded_child) = loaded_child_objects.get_mut(&id) {
566                    loaded_child.is_modified = true;
567                }
568                (id, (owner, type_, value))
569            })
570            .collect();
571        for deleted_id in &deleted_ids {
572            if let Some(loaded_child) = loaded_child_objects.get_mut(deleted_id) {
573                loaded_child.is_modified = true;
574            }
575        }
576
577        // Any received objects are viewed as modified. They had to be loaded in order to be
578        // received so they must be in the loaded_child_objects map otherwise it's an invariant
579        // violation.
580        for (received_object, _) in received.into_iter() {
581            match loaded_child_objects.get_mut(&received_object) {
582                Some(loaded_child) => {
583                    loaded_child.is_modified = true;
584                }
585                None => {
586                    return Err(ExecutionError::invariant_violation(format!(
587                        "Failed to find received UID {received_object} in loaded child objects."
588                    )))
589                }
590            }
591        }
592
593        Ok(RuntimeResults {
594            writes: written_objects,
595            user_events,
596            loaded_child_objects,
597            created_object_ids: new_ids,
598            deleted_object_ids: deleted_ids,
599        })
600    }
601
602    pub fn total_events_size(&self) -> u64 {
603        self.total_events_size
604    }
605
606    pub fn incr_total_events_size(&mut self, size: u64) {
607        self.total_events_size += size;
608    }
609}
610
611fn check_circular_ownership(
612    transfers: impl IntoIterator<Item = (ObjectID, Owner)>,
613) -> Result<(), ExecutionError> {
614    let mut object_owner_map = BTreeMap::new();
615    for (id, recipient) in transfers {
616        object_owner_map.remove(&id);
617        match recipient {
618            Owner::AddressOwner(_) | Owner::Shared { .. } | Owner::Immutable => (),
619            Owner::ObjectOwner(new_owner) => {
620                let new_owner: ObjectID = new_owner.into();
621                let mut cur = new_owner;
622                loop {
623                    if cur == id {
624                        return Err(ExecutionError::from_kind(
625                            ExecutionErrorKind::CircularObjectOwnership { object: cur },
626                        ));
627                    }
628                    if let Some(parent) = object_owner_map.get(&cur) {
629                        cur = *parent;
630                    } else {
631                        break;
632                    }
633                }
634                object_owner_map.insert(id, new_owner);
635            }
636            Owner::ConsensusAddressOwner { .. } => {
637                unimplemented!("ConsensusAddressOwner does not exist for this execution version")
638            }
639        }
640    }
641    Ok(())
642}
643
644/// WARNING! This function assumes that the bcs bytes have already been validated,
645/// and it will give an invariant violation otherwise.
646/// In short, we are relying on the invariant that the bytes are valid for objects
647/// in storage.  We do not need this invariant for dev-inspect, as the programmable
648/// transaction execution will validate the bytes before we get to this point.
649pub fn get_all_uids(
650    fully_annotated_layout: &MoveTypeLayout,
651    bcs_bytes: &[u8],
652) -> Result<BTreeSet<ObjectID>, /* invariant violation */ String> {
653    let mut ids = BTreeSet::new();
654    struct UIDTraversal<'i>(&'i mut BTreeSet<ObjectID>);
655    struct UIDCollector<'i>(&'i mut BTreeSet<ObjectID>);
656
657    impl<'b, 'l> AV::Traversal<'b, 'l> for UIDTraversal<'_> {
658        type Error = AV::Error;
659
660        fn traverse_struct(
661            &mut self,
662            driver: &mut AV::StructDriver<'_, 'b, 'l>,
663        ) -> Result<(), Self::Error> {
664            if driver.struct_layout().type_ == UID::type_() {
665                while driver.next_field(&mut UIDCollector(self.0))?.is_some() {}
666            } else {
667                while driver.next_field(self)?.is_some() {}
668            }
669            Ok(())
670        }
671    }
672
673    impl<'b, 'l> AV::Traversal<'b, 'l> for UIDCollector<'_> {
674        type Error = AV::Error;
675        fn traverse_address(
676            &mut self,
677            _driver: &AV::ValueDriver<'_, 'b, 'l>,
678            value: AccountAddress,
679        ) -> Result<(), Self::Error> {
680            self.0.insert(value.into());
681            Ok(())
682        }
683    }
684
685    MoveValue::visit_deserialize(
686        bcs_bytes,
687        fully_annotated_layout,
688        &mut UIDTraversal(&mut ids),
689    )
690    .map_err(|e| format!("Failed to deserialize. {e}"))?;
691    Ok(ids)
692}