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