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