Skip to main content

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