sui_adapter_v1/programmable_transactions/
context.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4pub use checked::*;
5
6#[sui_macros::with_checked_arithmetic]
7mod checked {
8    use std::{
9        borrow::Borrow,
10        collections::{BTreeMap, HashMap},
11        sync::Arc,
12    };
13
14    use crate::error::convert_vm_error;
15    use crate::execution_mode::ExecutionMode;
16    use crate::execution_value::{CommandKind, ObjectContents, TryFromValue, Value};
17    use crate::execution_value::{
18        ExecutionState, InputObjectMetadata, InputValue, ObjectValue, RawValueType, ResultValue,
19        UsageKind,
20    };
21    use crate::gas_charger::GasCharger;
22    use crate::programmable_transactions::linkage_view::LinkageView;
23    use crate::type_resolver::TypeTagResolver;
24    use crate::{adapter::new_native_extensions, gas_meter::SuiGasMeter};
25    use move_binary_format::{
26        errors::{Location, PartialVMError, PartialVMResult, VMError, VMResult},
27        file_format::{CodeOffset, FunctionDefinitionIndex, TypeParameterIndex},
28        CompiledModule,
29    };
30    use move_core_types::resolver::ModuleResolver;
31    use move_core_types::vm_status::StatusCode;
32    use move_core_types::{
33        account_address::AccountAddress,
34        identifier::IdentStr,
35        language_storage::{ModuleId, StructTag, TypeTag},
36    };
37    use move_vm_runtime::native_extensions::NativeContextExtensions;
38    use move_vm_runtime::{
39        move_vm::MoveVM,
40        session::{LoadedFunctionInstantiation, SerializedReturnValues},
41    };
42    use move_vm_types::data_store::DataStore;
43    use move_vm_types::loaded_data::runtime_types::Type;
44    use sui_move_natives::object_runtime::{
45        self, get_all_uids, max_event_error, LoadedRuntimeObject, ObjectRuntime, RuntimeResults,
46    };
47    use sui_protocol_config::ProtocolConfig;
48    use sui_types::execution::ExecutionResults;
49    use sui_types::storage::PackageObject;
50    use sui_types::{
51        balance::Balance,
52        base_types::{MoveObjectType, ObjectID, SuiAddress, TxContext},
53        coin::Coin,
54        error::{ExecutionError, ExecutionErrorKind},
55        event::Event,
56        execution::ExecutionResultsV2,
57        metrics::LimitsMetrics,
58        move_package::MovePackage,
59        object::{Data, MoveObject, Object, ObjectInner, Owner},
60        storage::BackingPackageStore,
61        transaction::{Argument, CallArg, ObjectArg},
62    };
63    use sui_types::{error::command_argument_error, execution_status::CommandArgumentError};
64    use tracing::instrument;
65
66    /// Maintains all runtime state specific to programmable transactions
67    pub struct ExecutionContext<'vm, 'state, 'a> {
68        /// The protocol config
69        pub protocol_config: &'a ProtocolConfig,
70        /// Metrics for reporting exceeded limits
71        pub metrics: Arc<LimitsMetrics>,
72        /// The MoveVM
73        pub vm: &'vm MoveVM,
74        /// The LinkageView for this session
75        pub linkage_view: LinkageView<'state>,
76        pub native_extensions: NativeContextExtensions<'state>,
77        /// The global state, used for resolving packages
78        pub state_view: &'state dyn ExecutionState,
79        /// A shared transaction context, contains transaction digest information and manages the
80        /// creation of new object IDs
81        pub tx_context: &'a mut TxContext,
82        /// The gas charger used for metering
83        pub gas_charger: &'a mut GasCharger,
84        /// Additional transfers not from the Move runtime
85        additional_transfers: Vec<(/* new owner */ SuiAddress, ObjectValue)>,
86        /// Newly published packages
87        new_packages: Vec<MovePackage>,
88        /// User events are claimed after each Move call
89        user_events: Vec<(ModuleId, StructTag, Vec<u8>)>,
90        // runtime data
91        /// The runtime value for the Gas coin, None if it has been taken/moved
92        gas: InputValue,
93        /// The runtime value for the inputs/call args, None if it has been taken/moved
94        inputs: Vec<InputValue>,
95        /// The results of a given command. For most commands, the inner vector will have length 1.
96        /// It will only not be 1 for Move calls with multiple return values.
97        /// Inner values are None if taken/moved by-value
98        results: Vec<Vec<ResultValue>>,
99        /// Map of arguments that are currently borrowed in this command, true if the borrow is mutable
100        /// This gets cleared out when new results are pushed, i.e. the end of a command
101        borrowed: HashMap<Argument, /* mut */ bool>,
102    }
103
104    /// A write for an object that was generated outside of the Move ObjectRuntime
105    struct AdditionalWrite {
106        /// The new owner of the object
107        recipient: Owner,
108        /// the type of the object,
109        type_: Type,
110        /// if the object has public transfer or not, i.e. if it has store
111        has_public_transfer: bool,
112        /// contents of the object
113        bytes: Vec<u8>,
114    }
115
116    impl<'vm, 'state, 'a> ExecutionContext<'vm, 'state, 'a> {
117        #[instrument(name = "ExecutionContext::new", level = "trace", skip_all)]
118        pub fn new(
119            protocol_config: &'a ProtocolConfig,
120            metrics: Arc<LimitsMetrics>,
121            vm: &'vm MoveVM,
122            state_view: &'state dyn ExecutionState,
123            tx_context: &'a mut TxContext,
124            gas_charger: &'a mut GasCharger,
125            inputs: Vec<CallArg>,
126        ) -> Result<Self, ExecutionError> {
127            let mut linkage_view = LinkageView::new(Box::new(state_view.as_sui_resolver()));
128            let mut input_object_map = BTreeMap::new();
129            let inputs = inputs
130                .into_iter()
131                .map(|call_arg| {
132                    load_call_arg(
133                        protocol_config,
134                        vm,
135                        state_view,
136                        &mut linkage_view,
137                        &[],
138                        &mut input_object_map,
139                        call_arg,
140                    )
141                })
142                .collect::<Result<_, ExecutionError>>()?;
143            let gas = if let Some(gas_coin) = gas_charger.gas_coin() {
144                let mut gas = load_object(
145                    protocol_config,
146                    vm,
147                    state_view,
148                    &mut linkage_view,
149                    &[],
150                    &mut input_object_map,
151                    /* imm override */ false,
152                    gas_coin,
153                )?;
154                // subtract the max gas budget. This amount is off limits in the programmable transaction,
155                // so to mimic this "off limits" behavior, we act as if the coin has less balance than
156                // it really does
157                let Some(Value::Object(ObjectValue {
158                    contents: ObjectContents::Coin(coin),
159                    ..
160                })) = &mut gas.inner.value
161                else {
162                    invariant_violation!("Gas object should be a populated coin")
163                };
164
165                let max_gas_in_balance = gas_charger.gas_budget();
166                let Some(new_balance) = coin.balance.value().checked_sub(max_gas_in_balance) else {
167                    invariant_violation!(
168                        "Transaction input checker should check that there is enough gas"
169                    );
170                };
171                coin.balance = Balance::new(new_balance);
172                gas
173            } else {
174                InputValue {
175                    object_metadata: None,
176                    inner: ResultValue {
177                        last_usage_kind: None,
178                        value: None,
179                    },
180                }
181            };
182            let native_extensions = new_native_extensions(
183                state_view.as_child_resolver(),
184                input_object_map,
185                !gas_charger.is_unmetered(),
186                protocol_config,
187                metrics.clone(),
188                tx_context.epoch(),
189            );
190
191            Ok(Self {
192                protocol_config,
193                metrics,
194                vm,
195                linkage_view,
196                native_extensions,
197                state_view,
198                tx_context,
199                gas_charger,
200                gas,
201                inputs,
202                results: vec![],
203                additional_transfers: vec![],
204                new_packages: vec![],
205                user_events: vec![],
206                borrowed: HashMap::new(),
207            })
208        }
209
210        pub fn object_runtime(&mut self) -> &ObjectRuntime<'_> {
211            self.native_extensions.get()
212        }
213
214        /// Create a new ID and update the state
215        pub fn fresh_id(&mut self) -> Result<ObjectID, ExecutionError> {
216            let object_id = self.tx_context.fresh_id();
217            let object_runtime: &mut ObjectRuntime = self.native_extensions.get_mut();
218            object_runtime
219                .new_id(object_id)
220                .map_err(|e| self.convert_vm_error(e.finish(Location::Undefined)))?;
221            Ok(object_id)
222        }
223
224        /// Delete an ID and update the state
225        pub fn delete_id(&mut self, object_id: ObjectID) -> Result<(), ExecutionError> {
226            let object_runtime: &mut ObjectRuntime = self.native_extensions.get_mut();
227            object_runtime
228                .delete_id(object_id)
229                .map_err(|e| self.convert_vm_error(e.finish(Location::Undefined)))
230        }
231
232        /// Set the link context for the session from the linkage information in the MovePackage found
233        /// at `package_id`.  Returns the runtime ID of the link context package on success.
234        pub fn set_link_context(
235            &mut self,
236            package_id: ObjectID,
237        ) -> Result<AccountAddress, ExecutionError> {
238            if self.linkage_view.has_linkage(package_id) {
239                // Setting same context again, can skip.
240                return Ok(self
241                    .linkage_view
242                    .original_package_id()
243                    .unwrap_or(*package_id));
244            }
245
246            let package = package_for_linkage(&self.linkage_view, package_id)
247                .map_err(|e| self.convert_vm_error(e))?;
248
249            self.linkage_view.set_linkage(package.move_package())
250        }
251
252        /// Load a type using the context's current session.
253        pub fn load_type(&mut self, type_tag: &TypeTag) -> VMResult<Type> {
254            load_type(
255                self.vm,
256                &mut self.linkage_view,
257                &self.new_packages,
258                type_tag,
259            )
260        }
261
262        /// Load a type using the context's current session.
263        pub fn load_type_from_struct(&mut self, struct_tag: &StructTag) -> VMResult<Type> {
264            load_type_from_struct(
265                self.vm,
266                &mut self.linkage_view,
267                &self.new_packages,
268                struct_tag,
269            )
270        }
271
272        /// Takes the user events from the runtime and tags them with the Move module of the function
273        /// that was invoked for the command
274        pub fn take_user_events(
275            &mut self,
276            module_id: &ModuleId,
277            function: FunctionDefinitionIndex,
278            last_offset: CodeOffset,
279        ) -> Result<(), ExecutionError> {
280            let object_runtime: &mut ObjectRuntime = self.native_extensions.get_mut();
281            let events = object_runtime.take_user_events();
282            let num_events = self.user_events.len() + events.len();
283            let max_events = self.protocol_config.max_num_event_emit();
284            if num_events as u64 > max_events {
285                let err = max_event_error(max_events)
286                    .at_code_offset(function, last_offset)
287                    .finish(Location::Module(module_id.clone()));
288                return Err(self.convert_vm_error(err));
289            }
290            let new_events = events
291                .into_iter()
292                .map(|(ty, tag, value)| {
293                    let layout = self
294                        .vm
295                        .get_runtime()
296                        .type_to_type_layout(&ty)
297                        .map_err(|e| self.convert_vm_error(e))?;
298                    let Some(bytes) = value.simple_serialize(&layout) else {
299                        invariant_violation!("Failed to deserialize already serialized Move value");
300                    };
301                    Ok((module_id.clone(), tag, bytes))
302                })
303                .collect::<Result<Vec<_>, ExecutionError>>()?;
304            self.user_events.extend(new_events);
305            Ok(())
306        }
307
308        /// Get the argument value. Cloning the value if it is copyable, and setting its value to None
309        /// if it is not (making it unavailable).
310        /// Errors if out of bounds, if the argument is borrowed, if it is unavailable (already taken),
311        /// or if it is an object that cannot be taken by value (shared or immutable)
312        pub fn by_value_arg<V: TryFromValue>(
313            &mut self,
314            command_kind: CommandKind<'_>,
315            arg_idx: usize,
316            arg: Argument,
317        ) -> Result<V, ExecutionError> {
318            self.by_value_arg_(command_kind, arg)
319                .map_err(|e| command_argument_error(e, arg_idx))
320        }
321        fn by_value_arg_<V: TryFromValue>(
322            &mut self,
323            command_kind: CommandKind<'_>,
324            arg: Argument,
325        ) -> Result<V, CommandArgumentError> {
326            let is_borrowed = self.arg_is_borrowed(&arg);
327            let (input_metadata_opt, val_opt) = self.borrow_mut(arg, UsageKind::ByValue)?;
328            let is_copyable = if let Some(val) = val_opt {
329                val.is_copyable()
330            } else {
331                return Err(CommandArgumentError::InvalidValueUsage);
332            };
333            // If it was taken, we catch this above.
334            // If it was not copyable and was borrowed, error as it creates a dangling reference in
335            // effect.
336            // We allow copyable values to be copied out even if borrowed, as we do not care about
337            // referential transparency at this level.
338            if !is_copyable && is_borrowed {
339                return Err(CommandArgumentError::InvalidValueUsage);
340            }
341            // Gas coin cannot be taken by value, except in TransferObjects
342            if matches!(arg, Argument::GasCoin)
343                && !matches!(command_kind, CommandKind::TransferObjects)
344            {
345                return Err(CommandArgumentError::InvalidGasCoinUsage);
346            }
347            // Immutable objects cannot be taken by value
348            if matches!(
349                input_metadata_opt,
350                Some(InputObjectMetadata::InputObject {
351                    owner: Owner::Immutable | Owner::Shared { .. },
352                    ..
353                })
354            ) {
355                return Err(CommandArgumentError::InvalidObjectByValue);
356            }
357
358            // Any input object taken by value must be mutable
359            if matches!(
360                input_metadata_opt,
361                Some(InputObjectMetadata::InputObject {
362                    is_mutable_input: false,
363                    ..
364                })
365            ) {
366                return Err(CommandArgumentError::InvalidObjectByValue);
367            }
368
369            let val = if is_copyable {
370                val_opt.as_ref().unwrap().clone()
371            } else {
372                val_opt.take().unwrap()
373            };
374            V::try_from_value(val)
375        }
376
377        /// Mimic a mutable borrow by taking the argument value, setting its value to None,
378        /// making it unavailable. The value will be marked as borrowed and must be returned with
379        /// restore_arg
380        /// Errors if out of bounds, if the argument is borrowed, if it is unavailable (already taken),
381        /// or if it is an object that cannot be mutably borrowed (immutable)
382        pub fn borrow_arg_mut<V: TryFromValue>(
383            &mut self,
384            arg_idx: usize,
385            arg: Argument,
386        ) -> Result<V, ExecutionError> {
387            self.borrow_arg_mut_(arg)
388                .map_err(|e| command_argument_error(e, arg_idx))
389        }
390        fn borrow_arg_mut_<V: TryFromValue>(
391            &mut self,
392            arg: Argument,
393        ) -> Result<V, CommandArgumentError> {
394            // mutable borrowing requires unique usage
395            if self.arg_is_borrowed(&arg) {
396                return Err(CommandArgumentError::InvalidValueUsage);
397            }
398            self.borrowed.insert(arg, /* is_mut */ true);
399            let (input_metadata_opt, val_opt) = self.borrow_mut(arg, UsageKind::BorrowMut)?;
400            let is_copyable = if let Some(val) = val_opt {
401                val.is_copyable()
402            } else {
403                // error if taken
404                return Err(CommandArgumentError::InvalidValueUsage);
405            };
406            if let Some(InputObjectMetadata::InputObject {
407                is_mutable_input: false,
408                ..
409            }) = input_metadata_opt
410            {
411                return Err(CommandArgumentError::InvalidObjectByMutRef);
412            }
413            // if it is copyable, don't take it as we allow for the value to be copied even if
414            // mutably borrowed
415            let val = if is_copyable {
416                val_opt.as_ref().unwrap().clone()
417            } else {
418                val_opt.take().unwrap()
419            };
420            V::try_from_value(val)
421        }
422
423        /// Mimics an immutable borrow by cloning the argument value without setting its value to None
424        /// Errors if out of bounds, if the argument is mutably borrowed,
425        /// or if it is unavailable (already taken)
426        pub fn borrow_arg<V: TryFromValue>(
427            &mut self,
428            arg_idx: usize,
429            arg: Argument,
430            type_: &Type,
431        ) -> Result<V, ExecutionError> {
432            self.borrow_arg_(arg, type_)
433                .map_err(|e| command_argument_error(e, arg_idx))
434        }
435        fn borrow_arg_<V: TryFromValue>(
436            &mut self,
437            arg: Argument,
438            arg_type: &Type,
439        ) -> Result<V, CommandArgumentError> {
440            // immutable borrowing requires the value was not mutably borrowed.
441            // If it was copied, that is okay.
442            // If it was taken/moved, we will find out below
443            if self.arg_is_mut_borrowed(&arg) {
444                return Err(CommandArgumentError::InvalidValueUsage);
445            }
446            self.borrowed.insert(arg, /* is_mut */ false);
447            let (_input_metadata_opt, val_opt) = self.borrow_mut(arg, UsageKind::BorrowImm)?;
448            if val_opt.is_none() {
449                return Err(CommandArgumentError::InvalidValueUsage);
450            }
451
452            // We eagerly reify receiving argument types at the first usage of them.
453            if let &mut Some(Value::Receiving(_, _, ref mut recv_arg_type @ None)) = val_opt {
454                let Type::Reference(inner) = arg_type else {
455                    return Err(CommandArgumentError::InvalidValueUsage);
456                };
457                *recv_arg_type = Some(*(*inner).clone());
458            }
459
460            V::try_from_value(val_opt.as_ref().unwrap().clone())
461        }
462
463        /// Restore an argument after being mutably borrowed
464        pub fn restore_arg<Mode: ExecutionMode>(
465            &mut self,
466            updates: &mut Mode::ArgumentUpdates,
467            arg: Argument,
468            value: Value,
469        ) -> Result<(), ExecutionError> {
470            Mode::add_argument_update(self, updates, arg, &value)?;
471            let was_mut_opt = self.borrowed.remove(&arg);
472            assert_invariant!(
473                was_mut_opt.is_some() && was_mut_opt.unwrap(),
474                "Should never restore a non-mut borrowed value. \
475                The take+restore is an implementation detail of mutable references"
476            );
477            // restore is exclusively used for mut
478            let Ok((_, value_opt)) = self.borrow_mut_impl(arg, None) else {
479                invariant_violation!("Should be able to borrow argument to restore it")
480            };
481
482            let old_value = value_opt.replace(value);
483            assert_invariant!(
484                old_value.is_none() || old_value.unwrap().is_copyable(),
485                "Should never restore a non-taken value, unless it is copyable. \
486                The take+restore is an implementation detail of mutable references"
487            );
488
489            Ok(())
490        }
491
492        /// Transfer the object to a new owner
493        pub fn transfer_object(
494            &mut self,
495            obj: ObjectValue,
496            addr: SuiAddress,
497        ) -> Result<(), ExecutionError> {
498            self.additional_transfers.push((addr, obj));
499            Ok(())
500        }
501
502        /// Create a new package
503        pub fn new_package<'p>(
504            &self,
505            modules: &[CompiledModule],
506            dependencies: impl IntoIterator<Item = &'p MovePackage>,
507        ) -> Result<MovePackage, ExecutionError> {
508            MovePackage::new_initial(modules, self.protocol_config, dependencies)
509        }
510
511        /// Create a package upgrade from `previous_package` with `new_modules` and `dependencies`
512        pub fn upgrade_package<'p>(
513            &self,
514            storage_id: ObjectID,
515            previous_package: &MovePackage,
516            new_modules: &[CompiledModule],
517            dependencies: impl IntoIterator<Item = &'p MovePackage>,
518        ) -> Result<MovePackage, ExecutionError> {
519            previous_package.new_upgraded(
520                storage_id,
521                new_modules,
522                self.protocol_config,
523                dependencies,
524            )
525        }
526
527        /// Add a newly created package to write as an effect of the transaction
528        pub fn write_package(&mut self, package: MovePackage) {
529            self.new_packages.push(package);
530        }
531
532        /// Return the last package pushed in `write_package`.
533        /// This function should be used in block of codes that push a package, verify
534        /// it, run the init and in case of error will remove the package.
535        /// The package has to be pushed for the init to run correctly.
536        pub fn pop_package(&mut self) -> Option<MovePackage> {
537            self.new_packages.pop()
538        }
539
540        /// Finish a command: clearing the borrows and adding the results to the result vector
541        pub fn push_command_results(&mut self, results: Vec<Value>) -> Result<(), ExecutionError> {
542            assert_invariant!(
543                self.borrowed.values().all(|is_mut| !is_mut),
544                "all mut borrows should be restored"
545            );
546            // clear borrow state
547            self.borrowed = HashMap::new();
548            self.results
549                .push(results.into_iter().map(ResultValue::new).collect());
550            Ok(())
551        }
552
553        /// Determine the object changes and collect all user events
554        pub fn finish<Mode: ExecutionMode>(self) -> Result<ExecutionResults, ExecutionError> {
555            let Self {
556                protocol_config,
557                vm,
558                linkage_view,
559                mut native_extensions,
560                tx_context,
561                gas_charger,
562                additional_transfers,
563                new_packages,
564                gas,
565                inputs,
566                results,
567                user_events,
568                ..
569            } = self;
570            let tx_digest = tx_context.digest();
571            let gas_id_opt = gas.object_metadata.as_ref().map(|info| info.id());
572            let mut loaded_runtime_objects = BTreeMap::new();
573            let mut additional_writes = BTreeMap::new();
574            for input in inputs.into_iter().chain(std::iter::once(gas)) {
575                let InputValue {
576                    object_metadata:
577                        Some(InputObjectMetadata::InputObject {
578                            // We are only interested in mutable inputs.
579                            is_mutable_input: true,
580                            id,
581                            version,
582                            owner,
583                        }),
584                    inner: ResultValue { value, .. },
585                } = input
586                else {
587                    continue;
588                };
589                loaded_runtime_objects.insert(
590                    id,
591                    LoadedRuntimeObject {
592                        version,
593                        is_modified: true,
594                    },
595                );
596                if let Some(Value::Object(object_value)) = value {
597                    add_additional_write(&mut additional_writes, owner, object_value)?;
598                }
599            }
600            // check for unused values
601            // disable this check for dev inspect
602            if !Mode::allow_arbitrary_values() {
603                for (i, command_result) in results.iter().enumerate() {
604                    for (j, result_value) in command_result.iter().enumerate() {
605                        let ResultValue {
606                            last_usage_kind,
607                            value,
608                        } = result_value;
609                        match value {
610                            None => (),
611                            Some(Value::Object(_)) => {
612                                return Err(ExecutionErrorKind::UnusedValueWithoutDrop {
613                                    result_idx: i as u16,
614                                    secondary_idx: j as u16,
615                                }
616                                .into())
617                            }
618                            Some(Value::Raw(RawValueType::Any, _)) => (),
619                            Some(Value::Raw(RawValueType::Loaded { abilities, .. }, _)) => {
620                                // - nothing to check for drop
621                                // - if it does not have drop, but has copy,
622                                //   the last usage must be by value in order to "lie" and say that the
623                                //   last usage is actually a take instead of a clone
624                                // - Otherwise, an error
625                                if abilities.has_drop()
626                                    || (abilities.has_copy()
627                                        && matches!(last_usage_kind, Some(UsageKind::ByValue)))
628                                {
629                                } else {
630                                    let msg = if abilities.has_copy() {
631                                        "The value has copy, but not drop. \
632                                        Its last usage must be by-value so it can be taken."
633                                    } else {
634                                        "Unused value without drop"
635                                    };
636                                    return Err(ExecutionError::new_with_source(
637                                        ExecutionErrorKind::UnusedValueWithoutDrop {
638                                            result_idx: i as u16,
639                                            secondary_idx: j as u16,
640                                        },
641                                        msg,
642                                    ));
643                                }
644                            }
645                            // Receiving arguments can be dropped without being received
646                            Some(Value::Receiving(_, _, _)) => (),
647                        }
648                    }
649                }
650            }
651            // add transfers from TransferObjects command
652            for (recipient, object_value) in additional_transfers {
653                let owner = Owner::AddressOwner(recipient);
654                add_additional_write(&mut additional_writes, owner, object_value)?;
655            }
656            // Refund unused gas
657            if let Some(gas_id) = gas_id_opt {
658                refund_max_gas_budget(&mut additional_writes, gas_charger, gas_id)?;
659            }
660
661            let object_runtime: ObjectRuntime = native_extensions.remove();
662
663            let RuntimeResults {
664                writes,
665                user_events: remaining_events,
666                loaded_child_objects,
667                mut created_object_ids,
668                deleted_object_ids,
669            } = object_runtime.finish()?;
670            assert_invariant!(
671                remaining_events.is_empty(),
672                "Events should be taken after every Move call"
673            );
674
675            loaded_runtime_objects.extend(loaded_child_objects);
676
677            let mut written_objects = BTreeMap::new();
678            for package in new_packages {
679                let package_obj = Object::new_from_package(package, tx_digest);
680                let id = package_obj.id();
681                created_object_ids.insert(id, ());
682                written_objects.insert(id, package_obj);
683            }
684            for (id, additional_write) in additional_writes {
685                let AdditionalWrite {
686                    recipient,
687                    type_,
688                    has_public_transfer,
689                    bytes,
690                } = additional_write;
691                // safe given the invariant that the runtime correctly propagates has_public_transfer
692                let move_object = unsafe {
693                    create_written_object(
694                        vm,
695                        &linkage_view,
696                        protocol_config,
697                        &loaded_runtime_objects,
698                        id,
699                        type_,
700                        has_public_transfer,
701                        bytes,
702                    )?
703                };
704                let object = Object::new_move(move_object, recipient, tx_digest);
705                written_objects.insert(id, object);
706                if let Some(loaded) = loaded_runtime_objects.get_mut(&id) {
707                    loaded.is_modified = true;
708                }
709            }
710
711            for (id, (recipient, ty, value)) in writes {
712                let abilities = vm
713                    .get_runtime()
714                    .get_type_abilities(&ty)
715                    .map_err(|e| convert_vm_error(e, vm, &linkage_view))?;
716                let has_public_transfer = abilities.has_store();
717                let layout = vm
718                    .get_runtime()
719                    .type_to_type_layout(&ty)
720                    .map_err(|e| convert_vm_error(e, vm, &linkage_view))?;
721                let Some(bytes) = value.simple_serialize(&layout) else {
722                    invariant_violation!("Failed to deserialize already serialized Move value");
723                };
724                // safe because has_public_transfer has been determined by the abilities
725                let move_object = unsafe {
726                    create_written_object(
727                        vm,
728                        &linkage_view,
729                        protocol_config,
730                        &loaded_runtime_objects,
731                        id,
732                        ty,
733                        has_public_transfer,
734                        bytes,
735                    )?
736                };
737                let object = Object::new_move(move_object, recipient, tx_digest);
738                written_objects.insert(id, object);
739            }
740
741            let user_events = user_events
742                .into_iter()
743                .map(|(module_id, tag, contents)| {
744                    Event::new(
745                        module_id.address(),
746                        module_id.name(),
747                        tx_context.sender(),
748                        tag,
749                        contents,
750                    )
751                })
752                .collect();
753
754            Ok(ExecutionResults::V2(ExecutionResultsV2 {
755                written_objects,
756                modified_objects: loaded_runtime_objects
757                    .into_iter()
758                    .filter_map(|(id, loaded)| loaded.is_modified.then_some(id))
759                    .collect(),
760                created_object_ids: created_object_ids.into_iter().map(|(id, _)| id).collect(),
761                deleted_object_ids: deleted_object_ids.into_iter().map(|(id, _)| id).collect(),
762                user_events,
763                // no accumulator events for v1
764                accumulator_events: vec![],
765                // no settlement input/output for v1
766                settlement_input_sui: 0,
767                settlement_output_sui: 0,
768            }))
769        }
770
771        /// Convert a VM Error to an execution one
772        pub fn convert_vm_error(&self, error: VMError) -> ExecutionError {
773            crate::error::convert_vm_error(error, self.vm, &self.linkage_view)
774        }
775
776        /// Special case errors for type arguments to Move functions
777        pub fn convert_type_argument_error(&self, idx: usize, error: VMError) -> ExecutionError {
778            use move_core_types::vm_status::StatusCode;
779            use sui_types::execution_status::TypeArgumentError;
780            match error.major_status() {
781                StatusCode::NUMBER_OF_TYPE_ARGUMENTS_MISMATCH => {
782                    ExecutionErrorKind::TypeArityMismatch.into()
783                }
784                StatusCode::TYPE_RESOLUTION_FAILURE => ExecutionErrorKind::TypeArgumentError {
785                    argument_idx: idx as TypeParameterIndex,
786                    kind: TypeArgumentError::TypeNotFound,
787                }
788                .into(),
789                StatusCode::CONSTRAINT_NOT_SATISFIED => ExecutionErrorKind::TypeArgumentError {
790                    argument_idx: idx as TypeParameterIndex,
791                    kind: TypeArgumentError::ConstraintNotSatisfied,
792                }
793                .into(),
794                _ => self.convert_vm_error(error),
795            }
796        }
797
798        /// Returns true if the value at the argument's location is borrowed, mutably or immutably
799        fn arg_is_borrowed(&self, arg: &Argument) -> bool {
800            self.borrowed.contains_key(arg)
801        }
802
803        /// Returns true if the value at the argument's location is mutably borrowed
804        fn arg_is_mut_borrowed(&self, arg: &Argument) -> bool {
805            matches!(self.borrowed.get(arg), Some(/* mut */ true))
806        }
807
808        /// Internal helper to borrow the value for an argument and update the most recent usage
809        fn borrow_mut(
810            &mut self,
811            arg: Argument,
812            usage: UsageKind,
813        ) -> Result<(Option<&InputObjectMetadata>, &mut Option<Value>), CommandArgumentError>
814        {
815            self.borrow_mut_impl(arg, Some(usage))
816        }
817
818        /// Internal helper to borrow the value for an argument
819        /// Updates the most recent usage if specified
820        fn borrow_mut_impl(
821            &mut self,
822            arg: Argument,
823            update_last_usage: Option<UsageKind>,
824        ) -> Result<(Option<&InputObjectMetadata>, &mut Option<Value>), CommandArgumentError>
825        {
826            let (metadata, result_value) = match arg {
827                Argument::GasCoin => (self.gas.object_metadata.as_ref(), &mut self.gas.inner),
828                Argument::Input(i) => {
829                    let Some(input_value) = self.inputs.get_mut(i as usize) else {
830                        return Err(CommandArgumentError::IndexOutOfBounds { idx: i });
831                    };
832                    (input_value.object_metadata.as_ref(), &mut input_value.inner)
833                }
834                Argument::Result(i) => {
835                    let Some(command_result) = self.results.get_mut(i as usize) else {
836                        return Err(CommandArgumentError::IndexOutOfBounds { idx: i });
837                    };
838                    if command_result.len() != 1 {
839                        return Err(CommandArgumentError::InvalidResultArity { result_idx: i });
840                    }
841                    (None, &mut command_result[0])
842                }
843                Argument::NestedResult(i, j) => {
844                    let Some(command_result) = self.results.get_mut(i as usize) else {
845                        return Err(CommandArgumentError::IndexOutOfBounds { idx: i });
846                    };
847                    let Some(result_value) = command_result.get_mut(j as usize) else {
848                        return Err(CommandArgumentError::SecondaryIndexOutOfBounds {
849                            result_idx: i,
850                            secondary_idx: j,
851                        });
852                    };
853                    (None, result_value)
854                }
855            };
856            if let Some(usage) = update_last_usage {
857                result_value.last_usage_kind = Some(usage);
858            }
859            Ok((metadata, &mut result_value.value))
860        }
861
862        pub(crate) fn execute_function_bypass_visibility(
863            &mut self,
864            module: &ModuleId,
865            function_name: &IdentStr,
866            ty_args: Vec<Type>,
867            args: Vec<impl Borrow<[u8]>>,
868        ) -> VMResult<SerializedReturnValues> {
869            let gas_status = self.gas_charger.move_gas_status_mut();
870            let mut data_store = SuiDataStore::new(&self.linkage_view, &self.new_packages);
871            self.vm.get_runtime().execute_function_bypass_visibility(
872                module,
873                function_name,
874                ty_args,
875                args,
876                &mut data_store,
877                &mut SuiGasMeter(gas_status),
878                &mut self.native_extensions,
879            )
880        }
881
882        pub(crate) fn load_function(
883            &mut self,
884            module_id: &ModuleId,
885            function_name: &IdentStr,
886            type_arguments: &[Type],
887        ) -> VMResult<LoadedFunctionInstantiation> {
888            let mut data_store = SuiDataStore::new(&self.linkage_view, &self.new_packages);
889            self.vm.get_runtime().load_function(
890                module_id,
891                function_name,
892                type_arguments,
893                &mut data_store,
894            )
895        }
896
897        pub(crate) fn make_object_value(
898            &mut self,
899            type_: MoveObjectType,
900            has_public_transfer: bool,
901            used_in_non_entry_move_call: bool,
902            contents: &[u8],
903        ) -> Result<ObjectValue, ExecutionError> {
904            make_object_value(
905                self.protocol_config,
906                self.vm,
907                &mut self.linkage_view,
908                &self.new_packages,
909                type_,
910                has_public_transfer,
911                used_in_non_entry_move_call,
912                contents,
913            )
914        }
915
916        pub fn publish_module_bundle(
917            &mut self,
918            modules: Vec<Vec<u8>>,
919            sender: AccountAddress,
920        ) -> VMResult<()> {
921            // TODO: publish_module_bundle() currently doesn't charge gas.
922            // Do we want to charge there?
923            let mut data_store = SuiDataStore::new(&self.linkage_view, &self.new_packages);
924            self.vm.get_runtime().publish_module_bundle(
925                modules,
926                sender,
927                &mut data_store,
928                &mut SuiGasMeter(self.gas_charger.move_gas_status_mut()),
929            )
930        }
931    }
932
933    impl TypeTagResolver for ExecutionContext<'_, '_, '_> {
934        fn get_type_tag(&self, type_: &Type) -> Result<TypeTag, ExecutionError> {
935            self.vm
936                .get_runtime()
937                .get_type_tag(type_)
938                .map_err(|e| self.convert_vm_error(e))
939        }
940    }
941
942    /// Fetch the package at `package_id` with a view to using it as a link context.  Produces an error
943    /// if the object at that ID does not exist, or is not a package.
944    fn package_for_linkage(
945        linkage_view: &LinkageView,
946        package_id: ObjectID,
947    ) -> VMResult<PackageObject> {
948        use move_binary_format::errors::PartialVMError;
949        use move_core_types::vm_status::StatusCode;
950
951        match linkage_view.get_package_object(&package_id) {
952            Ok(Some(package)) => Ok(package),
953            Ok(None) => Err(PartialVMError::new(StatusCode::LINKER_ERROR)
954                .with_message(format!("Cannot find link context {package_id} in store"))
955                .finish(Location::Undefined)),
956            Err(err) => Err(PartialVMError::new(StatusCode::LINKER_ERROR)
957                .with_message(format!(
958                    "Error loading link context {package_id} from store: {err}"
959                ))
960                .finish(Location::Undefined)),
961        }
962    }
963
964    pub fn load_type_from_struct(
965        vm: &MoveVM,
966        linkage_view: &mut LinkageView,
967        new_packages: &[MovePackage],
968        struct_tag: &StructTag,
969    ) -> VMResult<Type> {
970        fn verification_error<T>(code: StatusCode) -> VMResult<T> {
971            Err(PartialVMError::new(code).finish(Location::Undefined))
972        }
973
974        let StructTag {
975            address,
976            module,
977            name,
978            type_params,
979        } = struct_tag;
980
981        // Load the package that the struct is defined in, in storage
982        let defining_id = ObjectID::from_address(*address);
983        let package = package_for_linkage(linkage_view, defining_id)?;
984
985        // Set the defining package as the link context while loading the
986        // struct
987        let original_address = linkage_view
988            .set_linkage(package.move_package())
989            .map_err(|e| {
990                PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR)
991                    .with_message(e.to_string())
992                    .finish(Location::Undefined)
993            })?;
994
995        let runtime_id = ModuleId::new(original_address, module.clone());
996        let data_store = SuiDataStore::new(linkage_view, new_packages);
997        let res = vm.get_runtime().load_struct(&runtime_id, name, &data_store);
998        linkage_view.reset_linkage();
999        let (idx, struct_type) = res?;
1000
1001        // Recursively load type parameters, if necessary
1002        let type_param_constraints = struct_type.type_param_constraints();
1003        if type_param_constraints.len() != type_params.len() {
1004            return verification_error(StatusCode::NUMBER_OF_TYPE_ARGUMENTS_MISMATCH);
1005        }
1006
1007        if type_params.is_empty() {
1008            Ok(Type::Datatype(idx))
1009        } else {
1010            let loaded_type_params = type_params
1011                .iter()
1012                .map(|type_param| load_type(vm, linkage_view, new_packages, type_param))
1013                .collect::<VMResult<Vec<_>>>()?;
1014
1015            // Verify that the type parameter constraints on the struct are met
1016            for (constraint, param) in type_param_constraints.zip(&loaded_type_params) {
1017                let abilities = vm.get_runtime().get_type_abilities(param)?;
1018                if !constraint.is_subset(abilities) {
1019                    return verification_error(StatusCode::CONSTRAINT_NOT_SATISFIED);
1020                }
1021            }
1022
1023            Ok(Type::DatatypeInstantiation(Box::new((
1024                idx,
1025                loaded_type_params,
1026            ))))
1027        }
1028    }
1029
1030    /// Load `type_tag` to get a `Type` in the provided `session`.  `session`'s linkage context may be
1031    /// reset after this operation, because during the operation, it may change when loading a struct.
1032    pub fn load_type(
1033        vm: &MoveVM,
1034        linkage_view: &mut LinkageView,
1035        new_packages: &[MovePackage],
1036        type_tag: &TypeTag,
1037    ) -> VMResult<Type> {
1038        Ok(match type_tag {
1039            TypeTag::Bool => Type::Bool,
1040            TypeTag::U8 => Type::U8,
1041            TypeTag::U16 => Type::U16,
1042            TypeTag::U32 => Type::U32,
1043            TypeTag::U64 => Type::U64,
1044            TypeTag::U128 => Type::U128,
1045            TypeTag::U256 => Type::U256,
1046            TypeTag::Address => Type::Address,
1047            TypeTag::Signer => Type::Signer,
1048
1049            TypeTag::Vector(inner) => {
1050                Type::Vector(Box::new(load_type(vm, linkage_view, new_packages, inner)?))
1051            }
1052            TypeTag::Struct(struct_tag) => {
1053                return load_type_from_struct(vm, linkage_view, new_packages, struct_tag)
1054            }
1055        })
1056    }
1057
1058    pub(crate) fn make_object_value(
1059        protocol_config: &ProtocolConfig,
1060        vm: &MoveVM,
1061        linkage_view: &mut LinkageView,
1062        new_packages: &[MovePackage],
1063        type_: MoveObjectType,
1064        has_public_transfer: bool,
1065        used_in_non_entry_move_call: bool,
1066        contents: &[u8],
1067    ) -> Result<ObjectValue, ExecutionError> {
1068        let contents = if type_.is_coin() {
1069            let Ok(coin) = Coin::from_bcs_bytes(contents) else {
1070                invariant_violation!("Could not deserialize a coin")
1071            };
1072            ObjectContents::Coin(coin)
1073        } else {
1074            ObjectContents::Raw(contents.to_vec())
1075        };
1076
1077        let tag: StructTag = type_.into();
1078        let type_ = load_type_from_struct(vm, linkage_view, new_packages, &tag)
1079            .map_err(|e| crate::error::convert_vm_error(e, vm, linkage_view))?;
1080        let has_public_transfer = if protocol_config.recompute_has_public_transfer_in_execution() {
1081            let abilities = vm
1082                .get_runtime()
1083                .get_type_abilities(&type_)
1084                .map_err(|e| crate::error::convert_vm_error(e, vm, linkage_view))?;
1085            abilities.has_store()
1086        } else {
1087            has_public_transfer
1088        };
1089        Ok(ObjectValue {
1090            type_,
1091            has_public_transfer,
1092            used_in_non_entry_move_call,
1093            contents,
1094        })
1095    }
1096
1097    pub(crate) fn value_from_object(
1098        protocol_config: &ProtocolConfig,
1099        vm: &MoveVM,
1100        linkage_view: &mut LinkageView,
1101        new_packages: &[MovePackage],
1102        object: &Object,
1103    ) -> Result<ObjectValue, ExecutionError> {
1104        let ObjectInner {
1105            data: Data::Move(object),
1106            ..
1107        } = object.as_inner()
1108        else {
1109            invariant_violation!("Expected a Move object");
1110        };
1111
1112        let used_in_non_entry_move_call = false;
1113        make_object_value(
1114            protocol_config,
1115            vm,
1116            linkage_view,
1117            new_packages,
1118            object.type_().clone(),
1119            object.has_public_transfer(),
1120            used_in_non_entry_move_call,
1121            object.contents(),
1122        )
1123    }
1124
1125    /// Load an input object from the state_view
1126    fn load_object(
1127        protocol_config: &ProtocolConfig,
1128        vm: &MoveVM,
1129        state_view: &dyn ExecutionState,
1130        linkage_view: &mut LinkageView,
1131        new_packages: &[MovePackage],
1132        input_object_map: &mut BTreeMap<ObjectID, object_runtime::InputObject>,
1133        override_as_immutable: bool,
1134        id: ObjectID,
1135    ) -> Result<InputValue, ExecutionError> {
1136        let Some(obj) = state_view.read_object(&id) else {
1137            // protected by transaction input checker
1138            invariant_violation!("Object {} does not exist yet", id);
1139        };
1140        // override_as_immutable ==> Owner::Shared
1141        assert_invariant!(
1142            !override_as_immutable || matches!(obj.owner, Owner::Shared { .. }),
1143            "override_as_immutable should only be set for shared objects"
1144        );
1145        let is_mutable_input = match obj.owner {
1146            Owner::AddressOwner(_) => true,
1147            Owner::Shared { .. } => !override_as_immutable,
1148            Owner::Immutable => false,
1149            Owner::ObjectOwner(_) => {
1150                // protected by transaction input checker
1151                invariant_violation!("ObjectOwner objects cannot be input")
1152            }
1153            Owner::ConsensusAddressOwner { .. } => {
1154                unimplemented!("ConsensusAddressOwner does not exist for this execution version")
1155            }
1156        };
1157        let owner = obj.owner.clone();
1158        let version = obj.version();
1159        let object_metadata = InputObjectMetadata::InputObject {
1160            id,
1161            is_mutable_input,
1162            owner: owner.clone(),
1163            version,
1164        };
1165        let obj_value = value_from_object(protocol_config, vm, linkage_view, new_packages, obj)?;
1166        let contained_uids = {
1167            let fully_annotated_layout = vm
1168                .get_runtime()
1169                .type_to_fully_annotated_layout(&obj_value.type_)
1170                .map_err(|e| convert_vm_error(e, vm, linkage_view))?;
1171            let mut bytes = vec![];
1172            obj_value.write_bcs_bytes(&mut bytes);
1173            match get_all_uids(&fully_annotated_layout, &bytes) {
1174                Err(e) => {
1175                    invariant_violation!("Unable to retrieve UIDs for object. Got error: {e}")
1176                }
1177                Ok(uids) => uids,
1178            }
1179        };
1180        let runtime_input = object_runtime::InputObject {
1181            contained_uids,
1182            owner,
1183            version,
1184        };
1185        let prev = input_object_map.insert(id, runtime_input);
1186        // protected by transaction input checker
1187        assert_invariant!(prev.is_none(), "Duplicate input object {}", id);
1188        Ok(InputValue::new_object(object_metadata, obj_value))
1189    }
1190
1191    /// Load a CallArg, either an object or a raw set of BCS bytes
1192    fn load_call_arg(
1193        protocol_config: &ProtocolConfig,
1194        vm: &MoveVM,
1195        state_view: &dyn ExecutionState,
1196        linkage_view: &mut LinkageView,
1197        new_packages: &[MovePackage],
1198        input_object_map: &mut BTreeMap<ObjectID, object_runtime::InputObject>,
1199        call_arg: CallArg,
1200    ) -> Result<InputValue, ExecutionError> {
1201        Ok(match call_arg {
1202            CallArg::Pure(bytes) => InputValue::new_raw(RawValueType::Any, bytes),
1203            CallArg::Object(obj_arg) => load_object_arg(
1204                protocol_config,
1205                vm,
1206                state_view,
1207                linkage_view,
1208                new_packages,
1209                input_object_map,
1210                obj_arg,
1211            )?,
1212            CallArg::FundsWithdrawal(_) => {
1213                unreachable!("Impossible to hit BalanceWithdraw in v1")
1214            }
1215        })
1216    }
1217
1218    /// Load an ObjectArg from state view, marking if it can be treated as mutable or not
1219    fn load_object_arg(
1220        protocol_config: &ProtocolConfig,
1221        vm: &MoveVM,
1222        state_view: &dyn ExecutionState,
1223        linkage_view: &mut LinkageView,
1224        new_packages: &[MovePackage],
1225        input_object_map: &mut BTreeMap<ObjectID, object_runtime::InputObject>,
1226        obj_arg: ObjectArg,
1227    ) -> Result<InputValue, ExecutionError> {
1228        match obj_arg {
1229            ObjectArg::ImmOrOwnedObject((id, _, _)) => load_object(
1230                protocol_config,
1231                vm,
1232                state_view,
1233                linkage_view,
1234                new_packages,
1235                input_object_map,
1236                /* imm override */ false,
1237                id,
1238            ),
1239            ObjectArg::SharedObject { id, mutability, .. } => load_object(
1240                protocol_config,
1241                vm,
1242                state_view,
1243                linkage_view,
1244                new_packages,
1245                input_object_map,
1246                /* imm override */ !mutability.is_exclusive(),
1247                id,
1248            ),
1249            ObjectArg::Receiving((id, version, _)) => {
1250                Ok(InputValue::new_receiving_object(id, version))
1251            }
1252        }
1253    }
1254
1255    /// Generate an additional write for an ObjectValue
1256    fn add_additional_write(
1257        additional_writes: &mut BTreeMap<ObjectID, AdditionalWrite>,
1258        owner: Owner,
1259        object_value: ObjectValue,
1260    ) -> Result<(), ExecutionError> {
1261        let ObjectValue {
1262            type_,
1263            has_public_transfer,
1264            contents,
1265            ..
1266        } = object_value;
1267        let bytes = match contents {
1268            ObjectContents::Coin(coin) => coin.to_bcs_bytes(),
1269            ObjectContents::Raw(bytes) => bytes,
1270        };
1271        let object_id = MoveObject::id_opt(&bytes).map_err(|e| {
1272            ExecutionError::invariant_violation(format!("No id for Raw object bytes. {e}"))
1273        })?;
1274        let additional_write = AdditionalWrite {
1275            recipient: owner,
1276            type_,
1277            has_public_transfer,
1278            bytes,
1279        };
1280        additional_writes.insert(object_id, additional_write);
1281        Ok(())
1282    }
1283
1284    /// The max budget was deducted from the gas coin at the beginning of the transaction,
1285    /// now we return exactly that amount. Gas will be charged by the execution engine
1286    fn refund_max_gas_budget(
1287        additional_writes: &mut BTreeMap<ObjectID, AdditionalWrite>,
1288        gas_charger: &mut GasCharger,
1289        gas_id: ObjectID,
1290    ) -> Result<(), ExecutionError> {
1291        let Some(AdditionalWrite { bytes, .. }) = additional_writes.get_mut(&gas_id) else {
1292            invariant_violation!("Gas object cannot be wrapped or destroyed")
1293        };
1294        let Ok(mut coin) = Coin::from_bcs_bytes(bytes) else {
1295            invariant_violation!("Gas object must be a coin")
1296        };
1297        let Some(new_balance) = coin.balance.value().checked_add(gas_charger.gas_budget()) else {
1298            return Err(ExecutionError::new_with_source(
1299                ExecutionErrorKind::CoinBalanceOverflow,
1300                "Gas coin too large after returning the max gas budget",
1301            ));
1302        };
1303        coin.balance = Balance::new(new_balance);
1304        *bytes = coin.to_bcs_bytes();
1305        Ok(())
1306    }
1307
1308    /// Generate an MoveObject given an updated/written object
1309    /// # Safety
1310    ///
1311    /// This function assumes proper generation of has_public_transfer, either from the abilities of
1312    /// the StructTag, or from the runtime correctly propagating from the inputs
1313    unsafe fn create_written_object(
1314        vm: &MoveVM,
1315        linkage_view: &LinkageView,
1316        protocol_config: &ProtocolConfig,
1317        objects_modified_at: &BTreeMap<ObjectID, LoadedRuntimeObject>,
1318        id: ObjectID,
1319        type_: Type,
1320        has_public_transfer: bool,
1321        contents: Vec<u8>,
1322    ) -> Result<MoveObject, ExecutionError> {
1323        debug_assert_eq!(
1324            id,
1325            MoveObject::id_opt(&contents).expect("object contents should start with an id")
1326        );
1327        let old_obj_ver = objects_modified_at
1328            .get(&id)
1329            .map(|obj: &LoadedRuntimeObject| obj.version);
1330
1331        let type_tag = vm
1332            .get_runtime()
1333            .get_type_tag(&type_)
1334            .map_err(|e| crate::error::convert_vm_error(e, vm, linkage_view))?;
1335
1336        let struct_tag = match type_tag {
1337            TypeTag::Struct(inner) => *inner,
1338            _ => invariant_violation!("Non struct type for object"),
1339        };
1340        MoveObject::new_from_execution(
1341            struct_tag.into(),
1342            has_public_transfer,
1343            old_obj_ver.unwrap_or_default(),
1344            contents,
1345            protocol_config,
1346            /* system_mutation */ false,
1347        )
1348    }
1349
1350    // Implementation of the `DataStore` trait for the Move VM.
1351    // When used during execution it may have a list of new packages that have
1352    // just been published in the current context. Those are used for module/type
1353    // resolution when executing module init.
1354    // It may be created with an empty slice of packages either when no publish/upgrade
1355    // are performed or when a type is requested not during execution.
1356    pub(crate) struct SuiDataStore<'state, 'a> {
1357        linkage_view: &'a LinkageView<'state>,
1358        new_packages: &'a [MovePackage],
1359    }
1360
1361    impl<'state, 'a> SuiDataStore<'state, 'a> {
1362        pub(crate) fn new(
1363            linkage_view: &'a LinkageView<'state>,
1364            new_packages: &'a [MovePackage],
1365        ) -> Self {
1366            Self {
1367                linkage_view,
1368                new_packages,
1369            }
1370        }
1371
1372        fn get_module(&self, module_id: &ModuleId) -> Option<&Vec<u8>> {
1373            for package in self.new_packages {
1374                let module = package.get_module(module_id);
1375                if module.is_some() {
1376                    return module;
1377                }
1378            }
1379            None
1380        }
1381    }
1382
1383    // TODO: `DataStore` will be reworked and this is likely to disappear.
1384    //       Leaving this comment around until then as testament to better days to come...
1385    impl DataStore for SuiDataStore<'_, '_> {
1386        fn link_context(&self) -> AccountAddress {
1387            self.linkage_view.link_context()
1388        }
1389
1390        fn relocate(&self, module_id: &ModuleId) -> PartialVMResult<ModuleId> {
1391            self.linkage_view.relocate(module_id).map_err(|err| {
1392                PartialVMError::new(StatusCode::LINKER_ERROR)
1393                    .with_message(format!("Error relocating {module_id}: {err:?}"))
1394            })
1395        }
1396
1397        fn defining_module(
1398            &self,
1399            runtime_id: &ModuleId,
1400            struct_: &IdentStr,
1401        ) -> PartialVMResult<ModuleId> {
1402            self.linkage_view
1403                .defining_module(runtime_id, struct_)
1404                .map_err(|err| {
1405                    PartialVMError::new(StatusCode::LINKER_ERROR).with_message(format!(
1406                        "Error finding defining module for {runtime_id}::{struct_}: {err:?}"
1407                    ))
1408                })
1409        }
1410
1411        fn load_module(&self, module_id: &ModuleId) -> VMResult<Vec<u8>> {
1412            if let Some(bytes) = self.get_module(module_id) {
1413                return Ok(bytes.clone());
1414            }
1415            match self.linkage_view.get_module(module_id) {
1416                Ok(Some(bytes)) => Ok(bytes),
1417                Ok(None) => Err(PartialVMError::new(StatusCode::LINKER_ERROR)
1418                    .with_message(format!("Cannot find {:?} in data cache", module_id))
1419                    .finish(Location::Undefined)),
1420                Err(err) => {
1421                    let msg = format!("Unexpected storage error: {:?}", err);
1422                    Err(
1423                        PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR)
1424                            .with_message(msg)
1425                            .finish(Location::Undefined),
1426                    )
1427                }
1428            }
1429        }
1430
1431        fn publish_module(&mut self, _module_id: &ModuleId, _blob: Vec<u8>) -> VMResult<()> {
1432            // we cannot panic here because during execution and publishing this is
1433            // currently called from the publish flow in the Move runtime
1434            Ok(())
1435        }
1436    }
1437}