sui_adapter_latest/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]
7#[allow(clippy::type_complexity)]
8mod checked {
9    use crate::{
10        adapter::new_native_extensions,
11        data_store::{
12            PackageStore,
13            cached_package_store::CachedPackageStore,
14            legacy::{linkage_view::LinkageView, sui_data_store::SuiDataStore},
15        },
16        execution_mode::ExecutionMode,
17        execution_value::{
18            CommandKind, ExecutionState, InputObjectMetadata, InputValue, Mutability,
19            ObjectContents, ObjectValue, RawValueType, ResultValue, SizeBound, TryFromValue,
20            UsageKind, Value,
21        },
22        gas_charger::GasCharger,
23        gas_meter::SuiGasMeter,
24        type_resolver::TypeTagResolver,
25    };
26    use indexmap::IndexSet;
27    use move_binary_format::{
28        CompiledModule,
29        errors::{Location, PartialVMError, VMError, VMResult},
30        file_format::{AbilitySet, CodeOffset, FunctionDefinitionIndex, TypeParameterIndex},
31    };
32    use move_core_types::{
33        account_address::AccountAddress,
34        identifier::IdentStr,
35        language_storage::{ModuleId, StructTag, TypeTag},
36        resolver::MoveResolver,
37        u256::U256,
38        vm_status::StatusCode,
39    };
40    use move_trace_format::format::MoveTraceBuilder;
41    use move_vm_runtime::{
42        move_vm::MoveVM,
43        native_extensions::NativeContextExtensions,
44        session::{LoadedFunctionInstantiation, SerializedReturnValues},
45    };
46    use move_vm_types::loaded_data::runtime_types::Type;
47    use mysten_common::debug_fatal;
48    use std::{
49        borrow::Borrow,
50        cell::RefCell,
51        collections::{BTreeMap, BTreeSet, HashMap},
52        rc::Rc,
53        sync::Arc,
54    };
55    use sui_move_natives::object_runtime::{
56        self, LoadedRuntimeObject, MoveAccumulatorEvent, MoveAccumulatorValue, ObjectRuntime,
57        RuntimeResults, get_all_uids, max_event_error,
58    };
59    use sui_protocol_config::ProtocolConfig;
60    use sui_types::{
61        accumulator_event::AccumulatorEvent,
62        accumulator_root::AccumulatorObjId,
63        balance::Balance,
64        base_types::{MoveObjectType, ObjectID, SuiAddress, TxContext},
65        coin::Coin,
66        effects::{AccumulatorAddress, AccumulatorValue, AccumulatorWriteV1},
67        error::{ExecutionError, ExecutionErrorKind, SuiError, command_argument_error},
68        event::Event,
69        execution::{ExecutionResults, ExecutionResultsV2},
70        execution_status::CommandArgumentError,
71        funds_accumulator::Withdrawal,
72        metrics::LimitsMetrics,
73        move_package::MovePackage,
74        object::{Data, MoveObject, Object, ObjectInner, Owner},
75        storage::DenyListResult,
76        transaction::{
77            Argument, CallArg, FundsWithdrawalArg, ObjectArg, SharedObjectMutability, WithdrawFrom,
78        },
79    };
80    use tracing::instrument;
81
82    /// Maintains all runtime state specific to programmable transactions
83    pub struct ExecutionContext<'vm, 'state, 'a> {
84        /// The protocol config
85        pub protocol_config: &'a ProtocolConfig,
86        /// Metrics for reporting exceeded limits
87        pub metrics: Arc<LimitsMetrics>,
88        /// The MoveVM
89        pub vm: &'vm MoveVM,
90        /// The LinkageView for this session
91        pub linkage_view: LinkageView<'state>,
92        pub native_extensions: NativeContextExtensions<'state>,
93        /// The global state, used for resolving packages
94        pub state_view: &'state dyn ExecutionState,
95        /// A shared transaction context, contains transaction digest information and manages the
96        /// creation of new object IDs
97        pub tx_context: Rc<RefCell<TxContext>>,
98        /// The gas charger used for metering
99        pub gas_charger: &'a mut GasCharger,
100        /// Additional transfers not from the Move runtime
101        additional_transfers: Vec<(/* new owner */ SuiAddress, ObjectValue)>,
102        /// Newly published packages
103        new_packages: Vec<MovePackage>,
104        /// User events are claimed after each Move call
105        user_events: Vec<(ModuleId, StructTag, Vec<u8>)>,
106        // runtime data
107        /// The runtime value for the Gas coin, None if it has been taken/moved
108        gas: InputValue,
109        /// The runtime value for the inputs/call args, None if it has been taken/moved
110        inputs: Vec<InputValue>,
111        /// The results of a given command. For most commands, the inner vector will have length 1.
112        /// It will only not be 1 for Move calls with multiple return values.
113        /// Inner values are None if taken/moved by-value
114        results: Vec<Vec<ResultValue>>,
115        /// Map of arguments that are currently borrowed in this command, true if the borrow is mutable
116        /// This gets cleared out when new results are pushed, i.e. the end of a command
117        borrowed: HashMap<Arg, /* mut */ bool>,
118        /// Set of the by-value shared objects taken in the current command
119        per_command_by_value_shared_objects: BTreeSet<ObjectID>,
120    }
121
122    /// A write for an object that was generated outside of the Move ObjectRuntime
123    struct AdditionalWrite {
124        /// The new owner of the object
125        recipient: Owner,
126        /// the type of the object,
127        type_: Type,
128        /// if the object has public transfer or not, i.e. if it has store
129        has_public_transfer: bool,
130        /// contents of the object
131        bytes: Vec<u8>,
132    }
133
134    #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
135    pub struct Arg(Arg_);
136
137    #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
138    enum Arg_ {
139        V1(Argument),
140        V2(NormalizedArg),
141    }
142
143    #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
144    enum NormalizedArg {
145        GasCoin,
146        Input(u16),
147        Result(u16, u16),
148    }
149
150    impl<'vm, 'state, 'a> ExecutionContext<'vm, 'state, 'a> {
151        #[instrument(name = "ExecutionContext::new", level = "trace", skip_all)]
152        pub fn new(
153            protocol_config: &'a ProtocolConfig,
154            metrics: Arc<LimitsMetrics>,
155            vm: &'vm MoveVM,
156            state_view: &'state dyn ExecutionState,
157            tx_context: Rc<RefCell<TxContext>>,
158            gas_charger: &'a mut GasCharger,
159            inputs: Vec<CallArg>,
160        ) -> Result<Self, ExecutionError>
161        where
162            'a: 'state,
163        {
164            let mut linkage_view = LinkageView::new(Box::new(CachedPackageStore::new(Box::new(
165                state_view.as_sui_resolver(),
166            ))));
167            let mut input_object_map = BTreeMap::new();
168            let tx_context_ref = RefCell::borrow(&tx_context);
169            let inputs = inputs
170                .into_iter()
171                .map(|call_arg| {
172                    load_call_arg(
173                        protocol_config,
174                        vm,
175                        state_view,
176                        &mut linkage_view,
177                        &[],
178                        &mut input_object_map,
179                        &tx_context_ref,
180                        call_arg,
181                    )
182                })
183                .collect::<Result<_, ExecutionError>>()?;
184            std::mem::drop(tx_context_ref);
185            let gas = if let Some(gas_coin) = gas_charger.gas_coin() {
186                let mut gas = load_object(
187                    protocol_config,
188                    vm,
189                    state_view,
190                    &mut linkage_view,
191                    &[],
192                    &mut input_object_map,
193                    /* mutability override */ None,
194                    gas_coin,
195                )?;
196                // subtract the max gas budget. This amount is off limits in the programmable transaction,
197                // so to mimic this "off limits" behavior, we act as if the coin has less balance than
198                // it really does
199                let Some(Value::Object(ObjectValue {
200                    contents: ObjectContents::Coin(coin),
201                    ..
202                })) = &mut gas.inner.value
203                else {
204                    invariant_violation!("Gas object should be a populated coin")
205                };
206
207                let max_gas_in_balance = gas_charger.gas_budget();
208                let Some(new_balance) = coin.balance.value().checked_sub(max_gas_in_balance) else {
209                    invariant_violation!(
210                        "Transaction input checker should check that there is enough gas"
211                    );
212                };
213                coin.balance = Balance::new(new_balance);
214                gas
215            } else {
216                InputValue {
217                    object_metadata: None,
218                    inner: ResultValue {
219                        last_usage_kind: None,
220                        value: None,
221                        shared_object_ids: BTreeSet::new(),
222                    },
223                }
224            };
225            let native_extensions = new_native_extensions(
226                state_view.as_child_resolver(),
227                input_object_map,
228                !gas_charger.is_unmetered(),
229                protocol_config,
230                metrics.clone(),
231                tx_context.clone(),
232            );
233
234            Ok(Self {
235                protocol_config,
236                metrics,
237                vm,
238                linkage_view,
239                native_extensions,
240                state_view,
241                tx_context,
242                gas_charger,
243                gas,
244                inputs,
245                results: vec![],
246                additional_transfers: vec![],
247                new_packages: vec![],
248                user_events: vec![],
249                borrowed: HashMap::new(),
250                per_command_by_value_shared_objects: BTreeSet::new(),
251            })
252        }
253
254        pub fn object_runtime(&self) -> Result<&ObjectRuntime<'_>, ExecutionError> {
255            self.native_extensions
256                .get::<ObjectRuntime>()
257                .map_err(|e| self.convert_vm_error(e.finish(Location::Undefined)))
258        }
259
260        /// Create a new ID and update the state
261        pub fn fresh_id(&mut self) -> Result<ObjectID, ExecutionError> {
262            let object_id = self.tx_context.borrow_mut().fresh_id();
263            self.native_extensions
264                .get_mut()
265                .and_then(|object_runtime: &mut ObjectRuntime| object_runtime.new_id(object_id))
266                .map_err(|e| self.convert_vm_error(e.finish(Location::Undefined)))?;
267            Ok(object_id)
268        }
269
270        /// Delete an ID and update the state
271        pub fn delete_id(&mut self, object_id: ObjectID) -> Result<(), ExecutionError> {
272            self.native_extensions
273                .get_mut()
274                .and_then(|object_runtime: &mut ObjectRuntime| object_runtime.delete_id(object_id))
275                .map_err(|e| self.convert_vm_error(e.finish(Location::Undefined)))
276        }
277
278        /// Set the link context for the session from the linkage information in the MovePackage found
279        /// at `package_id`.  Returns the runtime ID of the link context package on success.
280        pub fn set_link_context(
281            &mut self,
282            package_id: ObjectID,
283        ) -> Result<AccountAddress, ExecutionError> {
284            if self.linkage_view.has_linkage(package_id)? {
285                // Setting same context again, can skip.
286                return Ok(self
287                    .linkage_view
288                    .original_package_id()?
289                    .unwrap_or(*package_id));
290            }
291
292            let move_package = get_package(&self.linkage_view, package_id)
293                .map_err(|e| self.convert_vm_error(e))?;
294
295            self.linkage_view.set_linkage(&move_package)
296        }
297
298        /// Load a type using the context's current session.
299        pub fn load_type(&mut self, type_tag: &TypeTag) -> VMResult<Type> {
300            load_type(self.vm, &self.linkage_view, &self.new_packages, type_tag)
301        }
302
303        /// Load a type using the context's current session.
304        pub fn load_type_from_struct(&mut self, struct_tag: &StructTag) -> VMResult<Type> {
305            load_type_from_struct(self.vm, &self.linkage_view, &self.new_packages, struct_tag)
306        }
307
308        pub fn get_type_abilities(&self, t: &Type) -> Result<AbilitySet, ExecutionError> {
309            self.vm
310                .get_runtime()
311                .get_type_abilities(t)
312                .map_err(|e| self.convert_vm_error(e))
313        }
314
315        /// Takes the user events from the runtime and tags them with the Move module of the function
316        /// that was invoked for the command
317        pub fn take_user_events(
318            &mut self,
319            module_id: &ModuleId,
320            function: FunctionDefinitionIndex,
321            last_offset: CodeOffset,
322        ) -> Result<(), ExecutionError> {
323            let events = self
324                .native_extensions
325                .get_mut()
326                .map(|object_runtime: &mut ObjectRuntime| object_runtime.take_user_events())
327                .map_err(|e| self.convert_vm_error(e.finish(Location::Undefined)))?;
328            let num_events = self.user_events.len() + events.len();
329            let max_events = self.protocol_config.max_num_event_emit();
330            if num_events as u64 > max_events {
331                let err = max_event_error(max_events)
332                    .at_code_offset(function, last_offset)
333                    .finish(Location::Module(module_id.clone()));
334                return Err(self.convert_vm_error(err));
335            }
336            let new_events = events
337                .into_iter()
338                .map(|(tag, value)| {
339                    let type_tag = TypeTag::Struct(Box::new(tag.clone()));
340                    let ty = unwrap_type_tag_load(
341                        self.protocol_config,
342                        self.vm
343                            .get_runtime()
344                            .try_load_cached_type(&type_tag)
345                            .map_err(|e| self.convert_vm_error(e))?
346                            .ok_or_else(|| {
347                                make_invariant_violation!(
348                                    "Failed to load type for event tag: {}",
349                                    tag
350                                )
351                            }),
352                    )?;
353                    let layout = self
354                        .vm
355                        .get_runtime()
356                        .type_to_type_layout(&ty)
357                        .map_err(|e| self.convert_vm_error(e))?;
358                    let Some(bytes) = value.typed_serialize(&layout) else {
359                        invariant_violation!("Failed to deserialize already serialized Move value");
360                    };
361                    Ok((module_id.clone(), tag, bytes))
362                })
363                .collect::<Result<Vec<_>, ExecutionError>>()?;
364            self.user_events.extend(new_events);
365            Ok(())
366        }
367
368        /// Takes an iterator of arguments and flattens a Result into a NestedResult if there
369        /// is more than one result.
370        /// However, it is currently gated to 1 result, so this function is in place for future
371        /// changes. This is currently blocked by more invasive work needed to update argument idx
372        /// in errors
373        pub fn splat_args<Items: IntoIterator<Item = Argument>>(
374            &self,
375            start_idx: usize,
376            args: Items,
377        ) -> Result<Vec<Arg>, ExecutionError>
378        where
379            Items::IntoIter: ExactSizeIterator,
380        {
381            if !self.protocol_config.normalize_ptb_arguments() {
382                Ok(args.into_iter().map(|arg| Arg(Arg_::V1(arg))).collect())
383            } else {
384                let args = args.into_iter();
385                let _args_len = args.len();
386                let mut res = vec![];
387                for (arg_idx, arg) in args.enumerate() {
388                    self.splat_arg(&mut res, arg)
389                        .map_err(|e| e.into_execution_error(start_idx + arg_idx))?;
390                }
391                debug_assert_eq!(res.len(), _args_len);
392                Ok(res)
393            }
394        }
395
396        fn splat_arg(&self, res: &mut Vec<Arg>, arg: Argument) -> Result<(), EitherError> {
397            match arg {
398                Argument::GasCoin => res.push(Arg(Arg_::V2(NormalizedArg::GasCoin))),
399                Argument::Input(i) => {
400                    if i as usize >= self.inputs.len() {
401                        return Err(CommandArgumentError::IndexOutOfBounds { idx: i }.into());
402                    }
403                    res.push(Arg(Arg_::V2(NormalizedArg::Input(i))))
404                }
405                Argument::NestedResult(i, j) => {
406                    let Some(command_result) = self.results.get(i as usize) else {
407                        return Err(CommandArgumentError::IndexOutOfBounds { idx: i }.into());
408                    };
409                    if j as usize >= command_result.len() {
410                        return Err(CommandArgumentError::SecondaryIndexOutOfBounds {
411                            result_idx: i,
412                            secondary_idx: j,
413                        }
414                        .into());
415                    };
416                    res.push(Arg(Arg_::V2(NormalizedArg::Result(i, j))))
417                }
418                Argument::Result(i) => {
419                    let Some(result) = self.results.get(i as usize) else {
420                        return Err(CommandArgumentError::IndexOutOfBounds { idx: i }.into());
421                    };
422                    let Ok(len): Result<u16, _> = result.len().try_into() else {
423                        invariant_violation!("Result of length greater than u16::MAX");
424                    };
425                    if len != 1 {
426                        // TODO protocol config to allow splatting of args
427                        return Err(
428                            CommandArgumentError::InvalidResultArity { result_idx: i }.into()
429                        );
430                    }
431                    res.extend((0..len).map(|j| Arg(Arg_::V2(NormalizedArg::Result(i, j)))))
432                }
433            }
434            Ok(())
435        }
436
437        pub fn one_arg(
438            &self,
439            command_arg_idx: usize,
440            arg: Argument,
441        ) -> Result<Arg, ExecutionError> {
442            let args = self.splat_args(command_arg_idx, vec![arg])?;
443            let Ok([arg]): Result<[Arg; 1], _> = args.try_into() else {
444                return Err(command_argument_error(
445                    CommandArgumentError::InvalidArgumentArity,
446                    command_arg_idx,
447                ));
448            };
449            Ok(arg)
450        }
451
452        /// Get the argument value. Cloning the value if it is copyable, and setting its value to None
453        /// if it is not (making it unavailable).
454        /// Errors if out of bounds, if the argument is borrowed, if it is unavailable (already taken),
455        /// or if it is an object that cannot be taken by value (shared or immutable)
456        pub fn by_value_arg<V: TryFromValue>(
457            &mut self,
458            command_kind: CommandKind,
459            arg_idx: usize,
460            arg: Arg,
461        ) -> Result<V, ExecutionError> {
462            self.by_value_arg_(command_kind, arg)
463                .map_err(|e| e.into_execution_error(arg_idx))
464        }
465        fn by_value_arg_<V: TryFromValue>(
466            &mut self,
467            command_kind: CommandKind,
468            arg: Arg,
469        ) -> Result<V, EitherError> {
470            let shared_obj_deletion_enabled = self.protocol_config.shared_object_deletion();
471            let per_command_shared_object_transfer_rules = self
472                .protocol_config
473                .per_command_shared_object_transfer_rules();
474            let is_borrowed = self.arg_is_borrowed(&arg);
475            let (input_metadata_opt, val_opt, shared_object_ids) =
476                self.borrow_mut(arg, UsageKind::ByValue)?;
477            let is_copyable = if let Some(val) = val_opt {
478                val.is_copyable()
479            } else {
480                return Err(CommandArgumentError::InvalidValueUsage.into());
481            };
482            // If it was taken, we catch this above.
483            // If it was not copyable and was borrowed, error as it creates a dangling reference in
484            // effect.
485            // We allow copyable values to be copied out even if borrowed, as we do not care about
486            // referential transparency at this level.
487            if !is_copyable && is_borrowed {
488                return Err(CommandArgumentError::InvalidValueUsage.into());
489            }
490            // Gas coin cannot be taken by value, except in TransferObjects
491            if arg.is_gas_coin() && !matches!(command_kind, CommandKind::TransferObjects) {
492                return Err(CommandArgumentError::InvalidGasCoinUsage.into());
493            }
494            // Immutable objects cannot be taken by value
495            if matches!(
496                input_metadata_opt,
497                Some(InputObjectMetadata::InputObject {
498                    owner: Owner::Immutable,
499                    ..
500                })
501            ) {
502                return Err(CommandArgumentError::InvalidObjectByValue.into());
503            }
504            if (
505                // this check can be removed after shared_object_deletion feature flag is removed
506                matches!(
507                    input_metadata_opt,
508                    Some(InputObjectMetadata::InputObject {
509                        owner: Owner::Shared { .. },
510                        ..
511                    })
512                ) && !shared_obj_deletion_enabled
513            ) {
514                return Err(CommandArgumentError::InvalidObjectByValue.into());
515            }
516
517            // Any input object taken by value must be exclusively mutable
518            match input_metadata_opt {
519                Some(InputObjectMetadata::InputObject { mutability, .. }) => match mutability {
520                    // NonExclusiveWrite can be taken by value, but unless it is re-shared
521                    // with no mutations, the transaction will abort.
522                    Mutability::Mutable | Mutability::NonExclusiveWrite => (),
523                    Mutability::Immutable => {
524                        return Err(CommandArgumentError::InvalidObjectByValue.into());
525                    }
526                },
527                Some(InputObjectMetadata::Receiving { .. }) => (),
528                None => (),
529            }
530
531            let val = if is_copyable {
532                val_opt.as_ref().unwrap().clone()
533            } else {
534                val_opt.take().unwrap()
535            };
536            if per_command_shared_object_transfer_rules {
537                // Track the shared object ID if `per_command_shared_object_transfer_rules`
538                // is enabled
539                let shared_object_ids = shared_object_ids.clone();
540                self.per_command_by_value_shared_objects
541                    .extend(shared_object_ids);
542            }
543            Ok(V::try_from_value(val)?)
544        }
545
546        /// Mimic a mutable borrow by taking the argument value, setting its value to None,
547        /// making it unavailable. The value will be marked as borrowed and must be returned with
548        /// restore_arg
549        /// Errors if out of bounds, if the argument is borrowed, if it is unavailable (already taken),
550        /// or if it is an object that cannot be mutably borrowed (immutable)
551        pub fn borrow_arg_mut<V: TryFromValue>(
552            &mut self,
553            arg_idx: usize,
554            arg: Arg,
555        ) -> Result<V, ExecutionError> {
556            self.borrow_arg_mut_(arg)
557                .map_err(|e| e.into_execution_error(arg_idx))
558        }
559        fn borrow_arg_mut_<V: TryFromValue>(&mut self, arg: Arg) -> Result<V, EitherError> {
560            let per_command_shared_object_transfer_rules = self
561                .protocol_config
562                .per_command_shared_object_transfer_rules();
563            // mutable borrowing requires unique usage
564            if self.arg_is_borrowed(&arg) {
565                return Err(CommandArgumentError::InvalidValueUsage.into());
566            }
567            self.borrowed.insert(arg, /* is_mut */ true);
568            let (input_metadata_opt, val_opt, shared_object_ids) =
569                self.borrow_mut(arg, UsageKind::BorrowMut)?;
570            let is_copyable = if let Some(val) = val_opt {
571                val.is_copyable()
572            } else {
573                // error if taken
574                return Err(CommandArgumentError::InvalidValueUsage.into());
575            };
576            match input_metadata_opt {
577                Some(InputObjectMetadata::InputObject { mutability, .. }) => match mutability {
578                    Mutability::Mutable | Mutability::NonExclusiveWrite => (),
579                    Mutability::Immutable => {
580                        return Err(CommandArgumentError::InvalidObjectByMutRef.into());
581                    }
582                },
583                Some(InputObjectMetadata::Receiving { .. }) => (),
584                None => (),
585            }
586            // if it is copyable, don't take it as we allow for the value to be copied even if
587            // mutably borrowed
588            let val = if is_copyable {
589                val_opt.as_ref().unwrap().clone()
590            } else {
591                val_opt.take().unwrap()
592            };
593            if per_command_shared_object_transfer_rules {
594                // track shared object IDs if `per_command_shared_object_transfer_rules`
595                // is enabled--but only for vectors from MakeMoveVec
596                let normalized_arg = match arg {
597                    Arg(Arg_::V2(arg)) => arg,
598                    Arg(Arg_::V1(_)) => {
599                        invariant_violation!(
600                            "v1 should not be used with per_command_shared_object_transfer_rules"
601                        )
602                    }
603                };
604                match normalized_arg {
605                    NormalizedArg::GasCoin | NormalizedArg::Input(_) => (),
606                    NormalizedArg::Result(_, _) => {
607                        // these will be populated only for results from MakeMoveVec, if the
608                        // vector is used mutably, we must assume that it could have been
609                        // taken from the vector
610                        let shared_object_ids = shared_object_ids.clone();
611                        self.per_command_by_value_shared_objects
612                            .extend(shared_object_ids);
613                    }
614                }
615            }
616            Ok(V::try_from_value(val)?)
617        }
618
619        /// Mimics an immutable borrow by cloning the argument value without setting its value to None
620        /// Errors if out of bounds, if the argument is mutably borrowed,
621        /// or if it is unavailable (already taken)
622        pub fn borrow_arg<V: TryFromValue>(
623            &mut self,
624            arg_idx: usize,
625            arg: Arg,
626            type_: &Type,
627        ) -> Result<V, ExecutionError> {
628            self.borrow_arg_(arg, type_)
629                .map_err(|e| e.into_execution_error(arg_idx))
630        }
631        fn borrow_arg_<V: TryFromValue>(
632            &mut self,
633            arg: Arg,
634            arg_type: &Type,
635        ) -> Result<V, EitherError> {
636            // immutable borrowing requires the value was not mutably borrowed.
637            // If it was copied, that is okay.
638            // If it was taken/moved, we will find out below
639            if self.arg_is_mut_borrowed(&arg) {
640                return Err(CommandArgumentError::InvalidValueUsage.into());
641            }
642            self.borrowed.insert(arg, /* is_mut */ false);
643            let (_input_metadata_opt, val_opt, _shared_object_ids) =
644                self.borrow_mut(arg, UsageKind::BorrowImm)?;
645            if val_opt.is_none() {
646                return Err(CommandArgumentError::InvalidValueUsage.into());
647            }
648
649            // We eagerly reify receiving argument types at the first usage of them.
650            if let &mut Some(Value::Receiving(_, _, ref mut recv_arg_type @ None)) = val_opt {
651                let Type::Reference(inner) = arg_type else {
652                    return Err(CommandArgumentError::InvalidValueUsage.into());
653                };
654                *recv_arg_type = Some(*(*inner).clone());
655            }
656
657            Ok(V::try_from_value(val_opt.as_ref().unwrap().clone())?)
658        }
659
660        /// Restore an argument after being mutably borrowed
661        pub fn restore_arg<Mode: ExecutionMode>(
662            &mut self,
663            updates: &mut Mode::ArgumentUpdates,
664            arg: Arg,
665            value: Value,
666        ) -> Result<(), ExecutionError> {
667            let per_command_shared_object_transfer_rules = self
668                .protocol_config
669                .per_command_shared_object_transfer_rules();
670            Mode::add_argument_update(self, updates, arg.into(), &value)?;
671            let was_mut_opt = self.borrowed.remove(&arg);
672            assert_invariant!(
673                was_mut_opt.is_some() && was_mut_opt.unwrap(),
674                "Should never restore a non-mut borrowed value. \
675                The take+restore is an implementation detail of mutable references"
676            );
677            // restore is exclusively used for mut
678            let Ok((_, value_opt, shared_object_ids)) = self.borrow_mut_impl(arg, None) else {
679                invariant_violation!("Should be able to borrow argument to restore it")
680            };
681            if per_command_shared_object_transfer_rules {
682                let normalized_arg = match arg {
683                    Arg(Arg_::V2(arg)) => arg,
684                    Arg(Arg_::V1(_)) => {
685                        invariant_violation!(
686                            "v1 should not be used with per_command_shared_object_transfer_rules"
687                        )
688                    }
689                };
690                match normalized_arg {
691                    NormalizedArg::GasCoin | NormalizedArg::Input(_) => (),
692                    NormalizedArg::Result(_, _) => {
693                        // these will be populated only for results from MakeMoveVec, if the
694                        // vector is used mutably, we must assume that it could have been
695                        // taken from the vector
696                        shared_object_ids.clear();
697                    }
698                }
699            }
700
701            let old_value = value_opt.replace(value);
702            assert_invariant!(
703                old_value.is_none() || old_value.unwrap().is_copyable(),
704                "Should never restore a non-taken value, unless it is copyable. \
705                The take+restore is an implementation detail of mutable references"
706            );
707
708            Ok(())
709        }
710
711        /// Transfer the object to a new owner
712        pub fn transfer_object(
713            &mut self,
714            obj: ObjectValue,
715            addr: SuiAddress,
716        ) -> Result<(), ExecutionError> {
717            self.additional_transfers.push((addr, obj));
718            Ok(())
719        }
720
721        /// Create a new package
722        pub fn new_package<'p>(
723            &self,
724            modules: &[CompiledModule],
725            dependencies: impl IntoIterator<Item = &'p MovePackage>,
726        ) -> Result<MovePackage, ExecutionError> {
727            MovePackage::new_initial(modules, self.protocol_config, dependencies)
728        }
729
730        /// Create a package upgrade from `previous_package` with `new_modules` and `dependencies`
731        pub fn upgrade_package<'p>(
732            &self,
733            storage_id: ObjectID,
734            previous_package: &MovePackage,
735            new_modules: &[CompiledModule],
736            dependencies: impl IntoIterator<Item = &'p MovePackage>,
737        ) -> Result<MovePackage, ExecutionError> {
738            previous_package.new_upgraded(
739                storage_id,
740                new_modules,
741                self.protocol_config,
742                dependencies,
743            )
744        }
745
746        /// Add a newly created package to write as an effect of the transaction
747        pub fn write_package(&mut self, package: MovePackage) {
748            self.new_packages.push(package);
749        }
750
751        /// Return the last package pushed in `write_package`.
752        /// This function should be used in block of codes that push a package, verify
753        /// it, run the init and in case of error will remove the package.
754        /// The package has to be pushed for the init to run correctly.
755        pub fn pop_package(&mut self) -> Option<MovePackage> {
756            self.new_packages.pop()
757        }
758
759        /// Finish a command: clearing the borrows and adding the results to the result vector
760        pub fn push_command_results(
761            &mut self,
762            command_kind: CommandKind,
763            mut results: Vec<Value>,
764        ) -> Result<(), ExecutionError> {
765            assert_invariant!(
766                self.borrowed.values().all(|is_mut| !is_mut),
767                "all mut borrows should be restored"
768            );
769            // clear borrow state
770            self.borrowed = HashMap::new();
771            let results = if self
772                .protocol_config
773                .per_command_shared_object_transfer_rules()
774            {
775                if let CommandKind::MakeMoveVec = command_kind {
776                    // For make MoveVec, we propagate the shared objects taken by value
777                    assert_invariant!(
778                        results.len() == 1,
779                        "MakeMoveVec should return a single result"
780                    );
781                    let mut result = ResultValue::new(results.pop().unwrap());
782                    result.shared_object_ids =
783                        std::mem::take(&mut self.per_command_by_value_shared_objects);
784                    vec![result]
785                } else {
786                    // For all other commands, we check the shared objects taken by value
787                    // are deleted or re-shared
788                    check_shared_object_usage(
789                        self.object_runtime()?,
790                        &self.per_command_by_value_shared_objects,
791                    )?;
792                    self.per_command_by_value_shared_objects.clear();
793                    results.into_iter().map(ResultValue::new).collect()
794                }
795            } else {
796                results.into_iter().map(ResultValue::new).collect()
797            };
798            self.results.push(results);
799            Ok(())
800        }
801
802        /// Determine the object changes and collect all user events
803        pub fn finish<Mode: ExecutionMode>(self) -> Result<ExecutionResults, ExecutionError> {
804            let Self {
805                protocol_config,
806                vm,
807                linkage_view,
808                mut native_extensions,
809                tx_context,
810                gas_charger,
811                additional_transfers,
812                new_packages,
813                gas,
814                inputs,
815                results,
816                user_events,
817                state_view,
818                ..
819            } = self;
820            let ref_context: &RefCell<TxContext> = tx_context.borrow();
821            let tx_digest = ref_context.borrow().digest();
822            let gas_id_opt = gas.object_metadata.as_ref().map(|info| info.id());
823            let mut loaded_runtime_objects = BTreeMap::new();
824            let mut additional_writes = BTreeMap::new();
825            let mut by_value_shared_objects = BTreeSet::new();
826            let mut consensus_owner_objects = BTreeMap::new();
827            for input in inputs.into_iter().chain(std::iter::once(gas)) {
828                let InputValue {
829                    object_metadata:
830                        Some(InputObjectMetadata::InputObject {
831                            // Both Mutable and NonExclusiveWrite are passed as &mut inputs.
832                            // Therefore, the type checker will allow mutation to either. It
833                            // is illegal to mutate NonExclusiveWrite objects, but we check this
834                            // post-execution.
835                            // Note that NonExclusiveWrite is not currently available to user
836                            // transactions.
837                            mutability: Mutability::Mutable | Mutability::NonExclusiveWrite,
838                            id,
839                            version,
840                            owner,
841                        }),
842                    inner: ResultValue { value, .. },
843                } = input
844                else {
845                    continue;
846                };
847                loaded_runtime_objects.insert(
848                    id,
849                    LoadedRuntimeObject {
850                        version,
851                        is_modified: true,
852                    },
853                );
854                if let Some(Value::Object(object_value)) = value {
855                    add_additional_write(&mut additional_writes, owner, object_value)?;
856                } else if owner.is_shared() {
857                    by_value_shared_objects.insert(id);
858                } else if matches!(owner, Owner::ConsensusAddressOwner { .. }) {
859                    consensus_owner_objects.insert(id, owner.clone());
860                }
861            }
862            // check for unused values
863            // disable this check for dev inspect
864            if !Mode::allow_arbitrary_values() {
865                for (i, command_result) in results.iter().enumerate() {
866                    for (j, result_value) in command_result.iter().enumerate() {
867                        let ResultValue {
868                            last_usage_kind,
869                            value,
870                            shared_object_ids: _,
871                        } = result_value;
872                        match value {
873                            None => (),
874                            Some(Value::Object(_)) => {
875                                return Err(ExecutionErrorKind::UnusedValueWithoutDrop {
876                                    result_idx: i as u16,
877                                    secondary_idx: j as u16,
878                                }
879                                .into());
880                            }
881                            Some(Value::Raw(RawValueType::Any, _)) => (),
882                            Some(Value::Raw(RawValueType::Loaded { abilities, .. }, _)) => {
883                                // - nothing to check for drop
884                                // - if it does not have drop, but has copy,
885                                //   the last usage must be by value in order to "lie" and say that the
886                                //   last usage is actually a take instead of a clone
887                                // - Otherwise, an error
888                                if abilities.has_drop()
889                                    || (abilities.has_copy()
890                                        && matches!(last_usage_kind, Some(UsageKind::ByValue)))
891                                {
892                                } else {
893                                    let msg = if abilities.has_copy() {
894                                        "The value has copy, but not drop. \
895                                        Its last usage must be by-value so it can be taken."
896                                    } else {
897                                        "Unused value without drop"
898                                    };
899                                    return Err(ExecutionError::new_with_source(
900                                        ExecutionErrorKind::UnusedValueWithoutDrop {
901                                            result_idx: i as u16,
902                                            secondary_idx: j as u16,
903                                        },
904                                        msg,
905                                    ));
906                                }
907                            }
908                            // Receiving arguments can be dropped without being received
909                            Some(Value::Receiving(_, _, _)) => (),
910                        }
911                    }
912                }
913            }
914            // add transfers from TransferObjects command
915            for (recipient, object_value) in additional_transfers {
916                let owner = Owner::AddressOwner(recipient);
917                add_additional_write(&mut additional_writes, owner, object_value)?;
918            }
919            // Refund unused gas
920            if let Some(gas_id) = gas_id_opt {
921                refund_max_gas_budget(&mut additional_writes, gas_charger, gas_id)?;
922            }
923
924            let object_runtime: ObjectRuntime = native_extensions.remove().map_err(|e| {
925                convert_vm_error(
926                    e.finish(Location::Undefined),
927                    vm,
928                    &linkage_view,
929                    protocol_config.resolve_abort_locations_to_package_id(),
930                )
931            })?;
932
933            let RuntimeResults {
934                writes,
935                user_events: remaining_events,
936                accumulator_events,
937                loaded_child_objects,
938                mut created_object_ids,
939                deleted_object_ids,
940                settlement_input_sui,
941                settlement_output_sui,
942            } = object_runtime.finish()?;
943            assert_invariant!(
944                remaining_events.is_empty(),
945                "Events should be taken after every Move call"
946            );
947
948            loaded_runtime_objects.extend(loaded_child_objects);
949
950            let mut written_objects = BTreeMap::new();
951            for package in new_packages {
952                let package_obj = Object::new_from_package(package, tx_digest);
953                let id = package_obj.id();
954                created_object_ids.insert(id);
955                written_objects.insert(id, package_obj);
956            }
957            for (id, additional_write) in additional_writes {
958                let AdditionalWrite {
959                    recipient,
960                    type_,
961                    has_public_transfer,
962                    bytes,
963                } = additional_write;
964                // safe given the invariant that the runtime correctly propagates has_public_transfer
965                let move_object = unsafe {
966                    create_written_object::<Mode>(
967                        vm,
968                        &linkage_view,
969                        protocol_config,
970                        &loaded_runtime_objects,
971                        id,
972                        type_,
973                        has_public_transfer,
974                        bytes,
975                    )?
976                };
977                let object = Object::new_move(move_object, recipient, tx_digest);
978                written_objects.insert(id, object);
979                if let Some(loaded) = loaded_runtime_objects.get_mut(&id) {
980                    loaded.is_modified = true;
981                }
982            }
983
984            for (id, (recipient, tag, value)) in writes {
985                let ty = unwrap_type_tag_load(
986                    protocol_config,
987                    vm.get_runtime()
988                        .try_load_cached_type(&TypeTag::from(tag.clone()))
989                        .map_err(|e| {
990                            convert_vm_error(
991                                e,
992                                vm,
993                                &linkage_view,
994                                protocol_config.resolve_abort_locations_to_package_id(),
995                            )
996                        })?
997                        .ok_or_else(|| {
998                            make_invariant_violation!("Failed to load type for event tag: {}", tag)
999                        }),
1000                )?;
1001                let abilities = vm.get_runtime().get_type_abilities(&ty).map_err(|e| {
1002                    convert_vm_error(
1003                        e,
1004                        vm,
1005                        &linkage_view,
1006                        protocol_config.resolve_abort_locations_to_package_id(),
1007                    )
1008                })?;
1009                let has_public_transfer = abilities.has_store();
1010                let layout = vm.get_runtime().type_to_type_layout(&ty).map_err(|e| {
1011                    convert_vm_error(
1012                        e,
1013                        vm,
1014                        &linkage_view,
1015                        protocol_config.resolve_abort_locations_to_package_id(),
1016                    )
1017                })?;
1018                let Some(bytes) = value.typed_serialize(&layout) else {
1019                    invariant_violation!("Failed to deserialize already serialized Move value");
1020                };
1021                // safe because has_public_transfer has been determined by the abilities
1022                let move_object = unsafe {
1023                    create_written_object::<Mode>(
1024                        vm,
1025                        &linkage_view,
1026                        protocol_config,
1027                        &loaded_runtime_objects,
1028                        id,
1029                        ty,
1030                        has_public_transfer,
1031                        bytes,
1032                    )?
1033                };
1034                let object = Object::new_move(move_object, recipient, tx_digest);
1035                written_objects.insert(id, object);
1036            }
1037
1038            finish(
1039                protocol_config,
1040                state_view,
1041                gas_charger,
1042                &ref_context.borrow(),
1043                &by_value_shared_objects,
1044                &consensus_owner_objects,
1045                loaded_runtime_objects,
1046                written_objects,
1047                created_object_ids,
1048                deleted_object_ids,
1049                user_events,
1050                accumulator_events,
1051                settlement_input_sui,
1052                settlement_output_sui,
1053            )
1054        }
1055
1056        /// Convert a VM Error to an execution one
1057        pub fn convert_vm_error(&self, error: VMError) -> ExecutionError {
1058            convert_vm_error(
1059                error,
1060                self.vm,
1061                &self.linkage_view,
1062                self.protocol_config.resolve_abort_locations_to_package_id(),
1063            )
1064        }
1065
1066        /// Special case errors for type arguments to Move functions
1067        pub fn convert_type_argument_error(&self, idx: usize, error: VMError) -> ExecutionError {
1068            convert_type_argument_error(
1069                idx,
1070                error,
1071                self.vm,
1072                &self.linkage_view,
1073                self.protocol_config.resolve_abort_locations_to_package_id(),
1074            )
1075        }
1076
1077        /// Returns true if the value at the argument's location is borrowed, mutably or immutably
1078        fn arg_is_borrowed(&self, arg: &Arg) -> bool {
1079            self.borrowed.contains_key(arg)
1080        }
1081
1082        /// Returns true if the value at the argument's location is mutably borrowed
1083        fn arg_is_mut_borrowed(&self, arg: &Arg) -> bool {
1084            matches!(self.borrowed.get(arg), Some(/* mut */ true))
1085        }
1086
1087        /// Internal helper to borrow the value for an argument and update the most recent usage
1088        fn borrow_mut(
1089            &mut self,
1090            arg: Arg,
1091            usage: UsageKind,
1092        ) -> Result<
1093            (
1094                Option<&InputObjectMetadata>,
1095                &mut Option<Value>,
1096                &mut BTreeSet<ObjectID>,
1097            ),
1098            EitherError,
1099        > {
1100            self.borrow_mut_impl(arg, Some(usage))
1101        }
1102
1103        /// Internal helper to borrow the value for an argument
1104        /// Updates the most recent usage if specified
1105        fn borrow_mut_impl(
1106            &mut self,
1107            arg: Arg,
1108            update_last_usage: Option<UsageKind>,
1109        ) -> Result<
1110            (
1111                Option<&InputObjectMetadata>,
1112                &mut Option<Value>,
1113                &mut BTreeSet<ObjectID>,
1114            ),
1115            EitherError,
1116        > {
1117            match arg.0 {
1118                Arg_::V1(arg) => {
1119                    assert_invariant!(
1120                        !self.protocol_config.normalize_ptb_arguments(),
1121                        "Should not be using v1 args with normalized args"
1122                    );
1123                    Ok(self.borrow_mut_impl_v1(arg, update_last_usage)?)
1124                }
1125                Arg_::V2(arg) => {
1126                    assert_invariant!(
1127                        self.protocol_config.normalize_ptb_arguments(),
1128                        "Should be using only v2 args with normalized args"
1129                    );
1130                    Ok(self.borrow_mut_impl_v2(arg, update_last_usage)?)
1131                }
1132            }
1133        }
1134
1135        // v1 of borrow_mut_impl
1136        fn borrow_mut_impl_v1(
1137            &mut self,
1138            arg: Argument,
1139            update_last_usage: Option<UsageKind>,
1140        ) -> Result<
1141            (
1142                Option<&InputObjectMetadata>,
1143                &mut Option<Value>,
1144                &mut BTreeSet<ObjectID>,
1145            ),
1146            CommandArgumentError,
1147        > {
1148            let (metadata, result_value) = match arg {
1149                Argument::GasCoin => (self.gas.object_metadata.as_ref(), &mut self.gas.inner),
1150                Argument::Input(i) => {
1151                    let Some(input_value) = self.inputs.get_mut(i as usize) else {
1152                        return Err(CommandArgumentError::IndexOutOfBounds { idx: i });
1153                    };
1154                    (input_value.object_metadata.as_ref(), &mut input_value.inner)
1155                }
1156                Argument::Result(i) => {
1157                    let Some(command_result) = self.results.get_mut(i as usize) else {
1158                        return Err(CommandArgumentError::IndexOutOfBounds { idx: i });
1159                    };
1160                    if command_result.len() != 1 {
1161                        return Err(CommandArgumentError::InvalidResultArity { result_idx: i });
1162                    }
1163                    (None, &mut command_result[0])
1164                }
1165                Argument::NestedResult(i, j) => {
1166                    let Some(command_result) = self.results.get_mut(i as usize) else {
1167                        return Err(CommandArgumentError::IndexOutOfBounds { idx: i });
1168                    };
1169                    let Some(result_value) = command_result.get_mut(j as usize) else {
1170                        return Err(CommandArgumentError::SecondaryIndexOutOfBounds {
1171                            result_idx: i,
1172                            secondary_idx: j,
1173                        });
1174                    };
1175                    (None, result_value)
1176                }
1177            };
1178            if let Some(usage) = update_last_usage {
1179                result_value.last_usage_kind = Some(usage);
1180            }
1181            Ok((
1182                metadata,
1183                &mut result_value.value,
1184                &mut result_value.shared_object_ids,
1185            ))
1186        }
1187
1188        // v2 of borrow_mut_impl
1189        fn borrow_mut_impl_v2(
1190            &mut self,
1191            arg: NormalizedArg,
1192            update_last_usage: Option<UsageKind>,
1193        ) -> Result<
1194            (
1195                Option<&InputObjectMetadata>,
1196                &mut Option<Value>,
1197                &mut BTreeSet<ObjectID>,
1198            ),
1199            ExecutionError,
1200        > {
1201            let (metadata, result_value) = match arg {
1202                NormalizedArg::GasCoin => (self.gas.object_metadata.as_ref(), &mut self.gas.inner),
1203                NormalizedArg::Input(i) => {
1204                    let input_value = self
1205                        .inputs
1206                        .get_mut(i as usize)
1207                        .ok_or_else(|| make_invariant_violation!("bounds already checked"))?;
1208                    let metadata = input_value.object_metadata.as_ref();
1209                    (metadata, &mut input_value.inner)
1210                }
1211                NormalizedArg::Result(i, j) => {
1212                    let result_value = self
1213                        .results
1214                        .get_mut(i as usize)
1215                        .ok_or_else(|| make_invariant_violation!("bounds already checked"))?
1216                        .get_mut(j as usize)
1217                        .ok_or_else(|| make_invariant_violation!("bounds already checked"))?;
1218                    (None, result_value)
1219                }
1220            };
1221            if let Some(usage) = update_last_usage {
1222                result_value.last_usage_kind = Some(usage);
1223            }
1224            Ok((
1225                metadata,
1226                &mut result_value.value,
1227                &mut result_value.shared_object_ids,
1228            ))
1229        }
1230
1231        pub(crate) fn execute_function_bypass_visibility(
1232            &mut self,
1233            module: &ModuleId,
1234            function_name: &IdentStr,
1235            ty_args: Vec<Type>,
1236            args: Vec<impl Borrow<[u8]>>,
1237            tracer: &mut Option<MoveTraceBuilder>,
1238        ) -> VMResult<SerializedReturnValues> {
1239            let gas_status = self.gas_charger.move_gas_status_mut();
1240            let mut data_store = SuiDataStore::new(&self.linkage_view, &self.new_packages);
1241            self.vm.get_runtime().execute_function_bypass_visibility(
1242                module,
1243                function_name,
1244                ty_args,
1245                args,
1246                &mut data_store,
1247                &mut SuiGasMeter(gas_status),
1248                &mut self.native_extensions,
1249                tracer.as_mut(),
1250            )
1251        }
1252
1253        pub(crate) fn load_function(
1254            &mut self,
1255            module_id: &ModuleId,
1256            function_name: &IdentStr,
1257            type_arguments: &[Type],
1258        ) -> VMResult<LoadedFunctionInstantiation> {
1259            let mut data_store = SuiDataStore::new(&self.linkage_view, &self.new_packages);
1260            self.vm.get_runtime().load_function(
1261                module_id,
1262                function_name,
1263                type_arguments,
1264                &mut data_store,
1265            )
1266        }
1267
1268        pub(crate) fn make_object_value(
1269            &mut self,
1270            type_: MoveObjectType,
1271            has_public_transfer: bool,
1272            used_in_non_entry_move_call: bool,
1273            contents: &[u8],
1274        ) -> Result<ObjectValue, ExecutionError> {
1275            make_object_value(
1276                self.protocol_config,
1277                self.vm,
1278                &mut self.linkage_view,
1279                &self.new_packages,
1280                type_,
1281                has_public_transfer,
1282                used_in_non_entry_move_call,
1283                contents,
1284            )
1285        }
1286
1287        pub fn publish_module_bundle(
1288            &mut self,
1289            modules: Vec<Vec<u8>>,
1290            sender: AccountAddress,
1291        ) -> VMResult<()> {
1292            // TODO: publish_module_bundle() currently doesn't charge gas.
1293            // Do we want to charge there?
1294            let mut data_store = SuiDataStore::new(&self.linkage_view, &self.new_packages);
1295            self.vm.get_runtime().publish_module_bundle(
1296                modules,
1297                sender,
1298                &mut data_store,
1299                &mut SuiGasMeter(self.gas_charger.move_gas_status_mut()),
1300            )
1301        }
1302
1303        pub fn size_bound_raw(&self, bound: u64) -> SizeBound {
1304            if self.protocol_config.max_ptb_value_size_v2() {
1305                SizeBound::Raw(bound)
1306            } else {
1307                SizeBound::Object(bound)
1308            }
1309        }
1310
1311        pub fn size_bound_vector_elem(&self, bound: u64) -> SizeBound {
1312            if self.protocol_config.max_ptb_value_size_v2() {
1313                SizeBound::VectorElem(bound)
1314            } else {
1315                SizeBound::Object(bound)
1316            }
1317        }
1318
1319        pub(crate) fn deserialize_modules(
1320            &self,
1321            module_bytes: &[Vec<u8>],
1322        ) -> Result<Vec<CompiledModule>, ExecutionError> {
1323            let binary_config = self.protocol_config.binary_config(None);
1324            let modules = module_bytes
1325                .iter()
1326                .map(|b| {
1327                    CompiledModule::deserialize_with_config(b, &binary_config)
1328                        .map_err(|e| e.finish(Location::Undefined))
1329                })
1330                .collect::<VMResult<Vec<CompiledModule>>>()
1331                .map_err(|e| self.convert_vm_error(e))?;
1332
1333            assert_invariant!(
1334                !modules.is_empty(),
1335                "input checker ensures package is not empty"
1336            );
1337
1338            Ok(modules)
1339        }
1340    }
1341
1342    impl Arg {
1343        fn is_gas_coin(&self) -> bool {
1344            // kept as two separate matches for exhaustiveness
1345            match self {
1346                Arg(Arg_::V1(a)) => matches!(a, Argument::GasCoin),
1347                Arg(Arg_::V2(n)) => matches!(n, NormalizedArg::GasCoin),
1348            }
1349        }
1350    }
1351
1352    impl From<Arg> for Argument {
1353        fn from(arg: Arg) -> Self {
1354            match arg.0 {
1355                Arg_::V1(a) => a,
1356                Arg_::V2(normalized) => match normalized {
1357                    NormalizedArg::GasCoin => Argument::GasCoin,
1358                    NormalizedArg::Input(i) => Argument::Input(i),
1359                    NormalizedArg::Result(i, j) => Argument::NestedResult(i, j),
1360                },
1361            }
1362        }
1363    }
1364
1365    impl TypeTagResolver for ExecutionContext<'_, '_, '_> {
1366        fn get_type_tag(&self, type_: &Type) -> Result<TypeTag, ExecutionError> {
1367            self.vm
1368                .get_runtime()
1369                .get_type_tag(type_)
1370                .map_err(|e| self.convert_vm_error(e))
1371        }
1372    }
1373
1374    /// Fetch the package at `package_id` with a view to using it as a link context.  Produces an error
1375    /// if the object at that ID does not exist, or is not a package.
1376    fn get_package(
1377        package_store: &dyn PackageStore,
1378        package_id: ObjectID,
1379    ) -> VMResult<Rc<MovePackage>> {
1380        match package_store.get_package(&package_id) {
1381            Ok(Some(package)) => Ok(package),
1382            Ok(None) => Err(PartialVMError::new(StatusCode::LINKER_ERROR)
1383                .with_message(format!("Cannot find link context {package_id} in store"))
1384                .finish(Location::Undefined)),
1385            Err(err) => Err(PartialVMError::new(StatusCode::LINKER_ERROR)
1386                .with_message(format!("Error loading {package_id} from store: {err}"))
1387                .finish(Location::Undefined)),
1388        }
1389    }
1390
1391    /// Check for valid shared object usage, either deleted or re-shared, at the end of a command
1392    pub fn check_shared_object_usage<'a>(
1393        object_runtime: &ObjectRuntime,
1394        consumed_shared_objects: impl IntoIterator<Item = &'a ObjectID>,
1395    ) -> Result<(), ExecutionError> {
1396        for id in consumed_shared_objects {
1397            // valid if done deleted or re-shared
1398            let is_valid_usage = object_runtime.is_deleted(id)
1399                || matches!(
1400                    object_runtime.is_transferred(id),
1401                    Some(Owner::Shared { .. })
1402                );
1403            if !is_valid_usage {
1404                return Err(ExecutionError::new_with_source(
1405                    ExecutionErrorKind::SharedObjectOperationNotAllowed,
1406                    format!(
1407                        "Shared object operation on {} not allowed: \
1408                        cannot be frozen, transferred, or wrapped",
1409                        id
1410                    ),
1411                ));
1412            }
1413        }
1414        Ok(())
1415    }
1416
1417    pub fn finish(
1418        protocol_config: &ProtocolConfig,
1419        state_view: &dyn ExecutionState,
1420        gas_charger: &mut GasCharger,
1421        tx_context: &TxContext,
1422        by_value_shared_objects: &BTreeSet<ObjectID>,
1423        consensus_owner_objects: &BTreeMap<ObjectID, Owner>,
1424        loaded_runtime_objects: BTreeMap<ObjectID, LoadedRuntimeObject>,
1425        written_objects: BTreeMap<ObjectID, Object>,
1426        created_object_ids: IndexSet<ObjectID>,
1427        deleted_object_ids: IndexSet<ObjectID>,
1428        user_events: Vec<(ModuleId, StructTag, Vec<u8>)>,
1429        accumulator_events: Vec<MoveAccumulatorEvent>,
1430        settlement_input_sui: u64,
1431        settlement_output_sui: u64,
1432    ) -> Result<ExecutionResults, ExecutionError> {
1433        // Before finishing, ensure that any shared object taken by value by the transaction is either:
1434        // 1. Mutated (and still has a shared ownership); or
1435        // 2. Deleted.
1436        // Otherwise, the shared object operation is not allowed and we fail the transaction.
1437        for id in by_value_shared_objects {
1438            // If it's been written it must have been reshared so must still have an ownership
1439            // of `Shared`.
1440            if let Some(obj) = written_objects.get(id) {
1441                if !obj.is_shared() {
1442                    if protocol_config.per_command_shared_object_transfer_rules() {
1443                        invariant_violation!(
1444                            "There should be no shared objects unaccounted for when \
1445                            per_command_shared_object_transfer_rules is enabled"
1446                        )
1447                    } else {
1448                        return Err(ExecutionError::new(
1449                            ExecutionErrorKind::SharedObjectOperationNotAllowed,
1450                            Some(
1451                                format!(
1452                                    "Shared object operation on {} not allowed: \
1453                                     cannot be frozen, transferred, or wrapped",
1454                                    id
1455                                )
1456                                .into(),
1457                            ),
1458                        ));
1459                    }
1460                }
1461            } else {
1462                // If it's not in the written objects, the object must have been deleted. Otherwise
1463                // it's an error.
1464                if !deleted_object_ids.contains(id) {
1465                    if protocol_config.per_command_shared_object_transfer_rules() {
1466                        invariant_violation!(
1467                            "There should be no shared objects unaccounted for when \
1468                            per_command_shared_object_transfer_rules is enabled"
1469                        )
1470                    } else {
1471                        return Err(ExecutionError::new(
1472                            ExecutionErrorKind::SharedObjectOperationNotAllowed,
1473                            Some(
1474                                format!("Shared object operation on {} not allowed: \
1475                                         shared objects used by value must be re-shared if not deleted", id).into(),
1476                            ),
1477                        ));
1478                    }
1479                }
1480            }
1481        }
1482
1483        // Before finishing, enforce auth restrictions on consensus objects.
1484        for (id, original_owner) in consensus_owner_objects {
1485            let Owner::ConsensusAddressOwner { owner, .. } = original_owner else {
1486                panic!(
1487                    "verified before adding to `consensus_owner_objects` that these are ConsensusAddressOwner"
1488                );
1489            };
1490            // Already verified in pre-execution checks that tx sender is the object owner.
1491            // Owner is allowed to do anything with the object.
1492            if tx_context.sender() != *owner {
1493                debug_fatal!(
1494                    "transaction with a singly owned input object where the tx sender is not the owner should never be executed"
1495                );
1496                if protocol_config.per_command_shared_object_transfer_rules() {
1497                    invariant_violation!(
1498                        "Shared object operation on {} not allowed: \
1499                        transaction with singly owned input object must be sent by the owner",
1500                        id,
1501                    );
1502                } else {
1503                    return Err(ExecutionError::new(
1504                                ExecutionErrorKind::SharedObjectOperationNotAllowed,
1505                                Some(
1506                                    format!("Shared object operation on {} not allowed: \
1507                                             transaction with singly owned input object must be sent by the owner", id).into(),
1508                                ),
1509                            ));
1510                }
1511            }
1512            // If an Owner type is implemented with support for more fine-grained authorization,
1513            // checks should be performed here. For example, transfers and wraps can be detected
1514            // by comparing `original_owner` with:
1515            // let new_owner = written_objects.get(&id).map(|obj| obj.owner);
1516            //
1517            // Deletions can be detected with:
1518            // let deleted = deleted_object_ids.contains(&id);
1519        }
1520
1521        let user_events: Vec<Event> = user_events
1522            .into_iter()
1523            .map(|(module_id, tag, contents)| {
1524                Event::new(
1525                    module_id.address(),
1526                    module_id.name(),
1527                    tx_context.sender(),
1528                    tag,
1529                    contents,
1530                )
1531            })
1532            .collect();
1533
1534        let mut receiving_funds_type_and_owners = BTreeMap::new();
1535        let accumulator_events = accumulator_events
1536            .into_iter()
1537            .map(|accum_event| {
1538                if let Some(ty) = Balance::maybe_get_balance_type_param(&accum_event.target_ty) {
1539                    receiving_funds_type_and_owners
1540                        .entry(ty)
1541                        .or_insert_with(BTreeSet::new)
1542                        .insert(accum_event.target_addr.into());
1543                }
1544                let value = match accum_event.value {
1545                    MoveAccumulatorValue::U64(amount) => AccumulatorValue::Integer(amount),
1546                    MoveAccumulatorValue::EventRef(event_idx) => {
1547                        let Some(event) = user_events.get(event_idx as usize) else {
1548                            invariant_violation!(
1549                                "Could not find authenticated event at index {}",
1550                                event_idx
1551                            );
1552                        };
1553                        let digest = event.digest();
1554                        AccumulatorValue::EventDigest(event_idx, digest)
1555                    }
1556                };
1557
1558                let address =
1559                    AccumulatorAddress::new(accum_event.target_addr.into(), accum_event.target_ty);
1560
1561                let write = AccumulatorWriteV1 {
1562                    address,
1563                    operation: accum_event.action.into_sui_accumulator_action(),
1564                    value,
1565                };
1566
1567                Ok(AccumulatorEvent::new(
1568                    AccumulatorObjId::new_unchecked(accum_event.accumulator_id),
1569                    write,
1570                ))
1571            })
1572            .collect::<Result<Vec<_>, ExecutionError>>()?;
1573
1574        if protocol_config.enable_coin_deny_list_v2() {
1575            for object in written_objects.values() {
1576                let coin_type = object.type_().and_then(|ty| ty.coin_type_maybe());
1577                let owner = object.owner.get_address_owner_address();
1578                if let (Some(ty), Ok(owner)) = (coin_type, owner) {
1579                    receiving_funds_type_and_owners
1580                        .entry(ty)
1581                        .or_insert_with(BTreeSet::new)
1582                        .insert(owner);
1583                }
1584            }
1585            let DenyListResult {
1586                result,
1587                num_non_gas_coin_owners,
1588            } = state_view.check_coin_deny_list(receiving_funds_type_and_owners);
1589            gas_charger.charge_coin_transfers(protocol_config, num_non_gas_coin_owners)?;
1590            result?;
1591        }
1592
1593        Ok(ExecutionResults::V2(ExecutionResultsV2 {
1594            written_objects,
1595            modified_objects: loaded_runtime_objects
1596                .into_iter()
1597                .filter_map(|(id, loaded)| loaded.is_modified.then_some(id))
1598                .collect(),
1599            created_object_ids: created_object_ids.into_iter().collect(),
1600            deleted_object_ids: deleted_object_ids.into_iter().collect(),
1601            user_events,
1602            accumulator_events,
1603            settlement_input_sui,
1604            settlement_output_sui,
1605        }))
1606    }
1607
1608    pub fn load_type_from_struct(
1609        vm: &MoveVM,
1610        linkage_view: &LinkageView,
1611        new_packages: &[MovePackage],
1612        struct_tag: &StructTag,
1613    ) -> VMResult<Type> {
1614        fn verification_error<T>(code: StatusCode) -> VMResult<T> {
1615            Err(PartialVMError::new(code).finish(Location::Undefined))
1616        }
1617
1618        let StructTag {
1619            address,
1620            module,
1621            name,
1622            type_params,
1623        } = struct_tag;
1624
1625        // Load the package that the struct is defined in, in storage
1626        let defining_id = ObjectID::from_address(*address);
1627
1628        let data_store = SuiDataStore::new(linkage_view, new_packages);
1629        let move_package = get_package(&data_store, defining_id)?;
1630
1631        // Set the defining package as the link context while loading the
1632        // struct
1633        let original_address = linkage_view.set_linkage(&move_package).map_err(|e| {
1634            PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR)
1635                .with_message(e.to_string())
1636                .finish(Location::Undefined)
1637        })?;
1638
1639        let runtime_id = ModuleId::new(original_address, module.clone());
1640        let data_store = SuiDataStore::new(linkage_view, new_packages);
1641        let res = vm.get_runtime().load_type(&runtime_id, name, &data_store);
1642        linkage_view.reset_linkage().map_err(|e| {
1643            PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR)
1644                .with_message(e.to_string())
1645                .finish(Location::Undefined)
1646        })?;
1647        let (idx, struct_type) = res?;
1648
1649        // Recursively load type parameters, if necessary
1650        let type_param_constraints = struct_type.type_param_constraints();
1651        if type_param_constraints.len() != type_params.len() {
1652            return verification_error(StatusCode::NUMBER_OF_TYPE_ARGUMENTS_MISMATCH);
1653        }
1654
1655        if type_params.is_empty() {
1656            Ok(Type::Datatype(idx))
1657        } else {
1658            let loaded_type_params = type_params
1659                .iter()
1660                .map(|type_param| load_type(vm, linkage_view, new_packages, type_param))
1661                .collect::<VMResult<Vec<_>>>()?;
1662
1663            // Verify that the type parameter constraints on the struct are met
1664            for (constraint, param) in type_param_constraints.zip(&loaded_type_params) {
1665                let abilities = vm.get_runtime().get_type_abilities(param)?;
1666                if !constraint.is_subset(abilities) {
1667                    return verification_error(StatusCode::CONSTRAINT_NOT_SATISFIED);
1668                }
1669            }
1670
1671            Ok(Type::DatatypeInstantiation(Box::new((
1672                idx,
1673                loaded_type_params,
1674            ))))
1675        }
1676    }
1677
1678    /// Load `type_tag` to get a `Type` in the provided `session`.  `session`'s linkage context may be
1679    /// reset after this operation, because during the operation, it may change when loading a struct.
1680    pub fn load_type(
1681        vm: &MoveVM,
1682        linkage_view: &LinkageView,
1683        new_packages: &[MovePackage],
1684        type_tag: &TypeTag,
1685    ) -> VMResult<Type> {
1686        Ok(match type_tag {
1687            TypeTag::Bool => Type::Bool,
1688            TypeTag::U8 => Type::U8,
1689            TypeTag::U16 => Type::U16,
1690            TypeTag::U32 => Type::U32,
1691            TypeTag::U64 => Type::U64,
1692            TypeTag::U128 => Type::U128,
1693            TypeTag::U256 => Type::U256,
1694            TypeTag::Address => Type::Address,
1695            TypeTag::Signer => Type::Signer,
1696
1697            TypeTag::Vector(inner) => {
1698                Type::Vector(Box::new(load_type(vm, linkage_view, new_packages, inner)?))
1699            }
1700            TypeTag::Struct(struct_tag) => {
1701                return load_type_from_struct(vm, linkage_view, new_packages, struct_tag);
1702            }
1703        })
1704    }
1705
1706    pub(crate) fn make_object_value(
1707        protocol_config: &ProtocolConfig,
1708        vm: &MoveVM,
1709        linkage_view: &mut LinkageView,
1710        new_packages: &[MovePackage],
1711        type_: MoveObjectType,
1712        has_public_transfer: bool,
1713        used_in_non_entry_move_call: bool,
1714        contents: &[u8],
1715    ) -> Result<ObjectValue, ExecutionError> {
1716        let contents = if type_.is_coin() {
1717            let Ok(coin) = Coin::from_bcs_bytes(contents) else {
1718                invariant_violation!("Could not deserialize a coin")
1719            };
1720            ObjectContents::Coin(coin)
1721        } else {
1722            ObjectContents::Raw(contents.to_vec())
1723        };
1724
1725        let tag: StructTag = type_.into();
1726        let type_ = load_type_from_struct(vm, linkage_view, new_packages, &tag).map_err(|e| {
1727            convert_vm_error(
1728                e,
1729                vm,
1730                linkage_view,
1731                protocol_config.resolve_abort_locations_to_package_id(),
1732            )
1733        })?;
1734        let has_public_transfer = if protocol_config.recompute_has_public_transfer_in_execution() {
1735            let abilities = vm.get_runtime().get_type_abilities(&type_).map_err(|e| {
1736                convert_vm_error(
1737                    e,
1738                    vm,
1739                    linkage_view,
1740                    protocol_config.resolve_abort_locations_to_package_id(),
1741                )
1742            })?;
1743            abilities.has_store()
1744        } else {
1745            has_public_transfer
1746        };
1747        Ok(ObjectValue {
1748            type_,
1749            has_public_transfer,
1750            used_in_non_entry_move_call,
1751            contents,
1752        })
1753    }
1754
1755    pub(crate) fn value_from_object(
1756        protocol_config: &ProtocolConfig,
1757        vm: &MoveVM,
1758        linkage_view: &mut LinkageView,
1759        new_packages: &[MovePackage],
1760        object: &Object,
1761    ) -> Result<ObjectValue, ExecutionError> {
1762        let ObjectInner {
1763            data: Data::Move(object),
1764            ..
1765        } = object.as_inner()
1766        else {
1767            invariant_violation!("Expected a Move object");
1768        };
1769
1770        let used_in_non_entry_move_call = false;
1771        make_object_value(
1772            protocol_config,
1773            vm,
1774            linkage_view,
1775            new_packages,
1776            object.type_().clone(),
1777            object.has_public_transfer(),
1778            used_in_non_entry_move_call,
1779            object.contents(),
1780        )
1781    }
1782
1783    /// Load an input object from the state_view
1784    fn load_object(
1785        protocol_config: &ProtocolConfig,
1786        vm: &MoveVM,
1787        state_view: &dyn ExecutionState,
1788        linkage_view: &mut LinkageView,
1789        new_packages: &[MovePackage],
1790        input_object_map: &mut BTreeMap<ObjectID, object_runtime::InputObject>,
1791        mutability_override: Option<Mutability>,
1792        id: ObjectID,
1793    ) -> Result<InputValue, ExecutionError> {
1794        let Some(obj) = state_view.read_object(&id) else {
1795            // protected by transaction input checker
1796            invariant_violation!("Object {} does not exist yet", id);
1797        };
1798        // override_as_immutable ==> Owner::Shared or Owner::ConsensusAddressOwner
1799        assert_invariant!(
1800            mutability_override.is_none()
1801                || matches!(
1802                    obj.owner,
1803                    Owner::Shared { .. } | Owner::ConsensusAddressOwner { .. }
1804                ),
1805            "override_as_immutable should only be set for consensus objects"
1806        );
1807        let mutability = match obj.owner {
1808            Owner::AddressOwner(_) => Mutability::Mutable,
1809            Owner::Shared { .. } | Owner::ConsensusAddressOwner { .. } => {
1810                mutability_override.unwrap_or(Mutability::Mutable)
1811            }
1812            Owner::Immutable => Mutability::Immutable,
1813            Owner::ObjectOwner(_) => {
1814                // protected by transaction input checker
1815                invariant_violation!("ObjectOwner objects cannot be input")
1816            }
1817        };
1818        let owner = obj.owner.clone();
1819        let version = obj.version();
1820        let object_metadata = InputObjectMetadata::InputObject {
1821            id,
1822            mutability,
1823            owner: owner.clone(),
1824            version,
1825        };
1826        let obj_value = value_from_object(protocol_config, vm, linkage_view, new_packages, obj)?;
1827        let contained_uids = {
1828            let fully_annotated_layout = vm
1829                .get_runtime()
1830                .type_to_fully_annotated_layout(&obj_value.type_)
1831                .map_err(|e| {
1832                    convert_vm_error(
1833                        e,
1834                        vm,
1835                        linkage_view,
1836                        protocol_config.resolve_abort_locations_to_package_id(),
1837                    )
1838                })?;
1839            let mut bytes = vec![];
1840            obj_value.write_bcs_bytes(&mut bytes, None)?;
1841            match get_all_uids(&fully_annotated_layout, &bytes) {
1842                Err(e) => {
1843                    invariant_violation!("Unable to retrieve UIDs for object. Got error: {e}")
1844                }
1845                Ok(uids) => uids,
1846            }
1847        };
1848        let runtime_input = object_runtime::InputObject {
1849            contained_uids,
1850            owner,
1851            version,
1852        };
1853        let prev = input_object_map.insert(id, runtime_input);
1854        // protected by transaction input checker
1855        assert_invariant!(prev.is_none(), "Duplicate input object {}", id);
1856        Ok(InputValue::new_object(object_metadata, obj_value))
1857    }
1858
1859    /// Load a CallArg, either an object or a raw set of BCS bytes
1860    fn load_call_arg(
1861        protocol_config: &ProtocolConfig,
1862        vm: &MoveVM,
1863        state_view: &dyn ExecutionState,
1864        linkage_view: &mut LinkageView,
1865        new_packages: &[MovePackage],
1866        input_object_map: &mut BTreeMap<ObjectID, object_runtime::InputObject>,
1867        tx_context: &TxContext,
1868        call_arg: CallArg,
1869    ) -> Result<InputValue, ExecutionError> {
1870        Ok(match call_arg {
1871            CallArg::Pure(bytes) => InputValue::new_raw(RawValueType::Any, bytes),
1872            CallArg::Object(obj_arg) => load_object_arg(
1873                protocol_config,
1874                vm,
1875                state_view,
1876                linkage_view,
1877                new_packages,
1878                input_object_map,
1879                obj_arg,
1880            )?,
1881            CallArg::FundsWithdrawal(FundsWithdrawalArg {
1882                reservation,
1883                type_arg,
1884                withdraw_from,
1885            }) => {
1886                let Ok(type_arg) = type_arg.to_type_tag() else {
1887                    // TODO(address-balances): ensure this is caught at signing
1888                    invariant_violation!(
1889                        "FundsWithdrawArg type arg should have been \
1890                        checked at signing"
1891                    );
1892                };
1893                let withdrawal_ty = Withdrawal::type_tag(type_arg);
1894                let ty =
1895                    load_type(vm, linkage_view, new_packages, &withdrawal_ty).map_err(|e| {
1896                        convert_type_argument_error(
1897                            0,
1898                            e,
1899                            vm,
1900                            linkage_view,
1901                            protocol_config.resolve_abort_locations_to_package_id(),
1902                        )
1903                    })?;
1904                let abilities = vm.get_runtime().get_type_abilities(&ty).map_err(|e| {
1905                    convert_vm_error(
1906                        e,
1907                        vm,
1908                        linkage_view,
1909                        protocol_config.resolve_abort_locations_to_package_id(),
1910                    )
1911                })?;
1912                let loaded_ty = RawValueType::Loaded {
1913                    ty,
1914                    abilities,
1915                    used_in_non_entry_move_call: false,
1916                };
1917                let owner = match withdraw_from {
1918                    WithdrawFrom::Sender => tx_context.sender(),
1919                    WithdrawFrom::Sponsor => {
1920                        invariant_violation!(
1921                            "WithdrawFrom::Sponsor call arg not supported, \
1922                            should have been checked at signing"
1923                        );
1924                    }
1925                };
1926                // After this point, we can treat this like any other returned/loaded value, e.g.
1927                // from a Move call. As such, sanity check Withdrawal should have only drop.
1928                debug_assert!({
1929                    !abilities.has_key()
1930                        && !abilities.has_store()
1931                        && !abilities.has_copy()
1932                        && abilities.has_drop()
1933                });
1934                let limit = match reservation {
1935                    sui_types::transaction::Reservation::EntireBalance => {
1936                        // TODO(address-balances): support entire balance withdrawal
1937                        todo!("Entire balance withdrawal not yet supported")
1938                    }
1939                    sui_types::transaction::Reservation::MaxAmountU64(u) => U256::from(u),
1940                };
1941                InputValue::withdrawal(loaded_ty, owner, limit)
1942            }
1943        })
1944    }
1945
1946    /// Load an ObjectArg from state view, marking if it can be treated as mutable or not
1947    fn load_object_arg(
1948        protocol_config: &ProtocolConfig,
1949        vm: &MoveVM,
1950        state_view: &dyn ExecutionState,
1951        linkage_view: &mut LinkageView,
1952        new_packages: &[MovePackage],
1953        input_object_map: &mut BTreeMap<ObjectID, object_runtime::InputObject>,
1954        obj_arg: ObjectArg,
1955    ) -> Result<InputValue, ExecutionError> {
1956        match obj_arg {
1957            ObjectArg::ImmOrOwnedObject((id, _, _)) => load_object(
1958                protocol_config,
1959                vm,
1960                state_view,
1961                linkage_view,
1962                new_packages,
1963                input_object_map,
1964                /* mutability override */ None,
1965                id,
1966            ),
1967            ObjectArg::SharedObject { id, mutability, .. } => {
1968                let mutability = match mutability {
1969                    SharedObjectMutability::Mutable => Mutability::Mutable,
1970                    // From the perspective of the adapter, non-exclusive write are mutable,
1971                    // in that the move code will receive a &mut arg. The object itself
1972                    // cannot be written to, this is enforced by post execution checks.
1973                    SharedObjectMutability::NonExclusiveWrite => Mutability::NonExclusiveWrite,
1974                    SharedObjectMutability::Immutable => Mutability::Immutable,
1975                };
1976                load_object(
1977                    protocol_config,
1978                    vm,
1979                    state_view,
1980                    linkage_view,
1981                    new_packages,
1982                    input_object_map,
1983                    Some(mutability),
1984                    id,
1985                )
1986            }
1987            ObjectArg::Receiving((id, version, _)) => {
1988                Ok(InputValue::new_receiving_object(id, version))
1989            }
1990        }
1991    }
1992
1993    /// Generate an additional write for an ObjectValue
1994    fn add_additional_write(
1995        additional_writes: &mut BTreeMap<ObjectID, AdditionalWrite>,
1996        owner: Owner,
1997        object_value: ObjectValue,
1998    ) -> Result<(), ExecutionError> {
1999        let ObjectValue {
2000            type_,
2001            has_public_transfer,
2002            contents,
2003            ..
2004        } = object_value;
2005        let bytes = match contents {
2006            ObjectContents::Coin(coin) => coin.to_bcs_bytes(),
2007            ObjectContents::Raw(bytes) => bytes,
2008        };
2009        let object_id = MoveObject::id_opt(&bytes).map_err(|e| {
2010            ExecutionError::invariant_violation(format!("No id for Raw object bytes. {e}"))
2011        })?;
2012        let additional_write = AdditionalWrite {
2013            recipient: owner,
2014            type_,
2015            has_public_transfer,
2016            bytes,
2017        };
2018        additional_writes.insert(object_id, additional_write);
2019        Ok(())
2020    }
2021
2022    /// The max budget was deducted from the gas coin at the beginning of the transaction,
2023    /// now we return exactly that amount. Gas will be charged by the execution engine
2024    fn refund_max_gas_budget(
2025        additional_writes: &mut BTreeMap<ObjectID, AdditionalWrite>,
2026        gas_charger: &mut GasCharger,
2027        gas_id: ObjectID,
2028    ) -> Result<(), ExecutionError> {
2029        let Some(AdditionalWrite { bytes, .. }) = additional_writes.get_mut(&gas_id) else {
2030            invariant_violation!("Gas object cannot be wrapped or destroyed")
2031        };
2032        let Ok(mut coin) = Coin::from_bcs_bytes(bytes) else {
2033            invariant_violation!("Gas object must be a coin")
2034        };
2035        let Some(new_balance) = coin.balance.value().checked_add(gas_charger.gas_budget()) else {
2036            return Err(ExecutionError::new_with_source(
2037                ExecutionErrorKind::CoinBalanceOverflow,
2038                "Gas coin too large after returning the max gas budget",
2039            ));
2040        };
2041        coin.balance = Balance::new(new_balance);
2042        *bytes = coin.to_bcs_bytes();
2043        Ok(())
2044    }
2045
2046    /// Generate an MoveObject given an updated/written object
2047    /// # Safety
2048    ///
2049    /// This function assumes proper generation of has_public_transfer, either from the abilities of
2050    /// the StructTag, or from the runtime correctly propagating from the inputs
2051    unsafe fn create_written_object<Mode: ExecutionMode>(
2052        vm: &MoveVM,
2053        linkage_view: &LinkageView,
2054        protocol_config: &ProtocolConfig,
2055        objects_modified_at: &BTreeMap<ObjectID, LoadedRuntimeObject>,
2056        id: ObjectID,
2057        type_: Type,
2058        has_public_transfer: bool,
2059        contents: Vec<u8>,
2060    ) -> Result<MoveObject, ExecutionError> {
2061        debug_assert_eq!(
2062            id,
2063            MoveObject::id_opt(&contents).expect("object contents should start with an id")
2064        );
2065        let old_obj_ver = objects_modified_at
2066            .get(&id)
2067            .map(|obj: &LoadedRuntimeObject| obj.version);
2068
2069        let type_tag = vm.get_runtime().get_type_tag(&type_).map_err(|e| {
2070            convert_vm_error(
2071                e,
2072                vm,
2073                linkage_view,
2074                protocol_config.resolve_abort_locations_to_package_id(),
2075            )
2076        })?;
2077
2078        let struct_tag = match type_tag {
2079            TypeTag::Struct(inner) => *inner,
2080            _ => invariant_violation!("Non struct type for object"),
2081        };
2082        unsafe {
2083            MoveObject::new_from_execution(
2084                struct_tag.into(),
2085                has_public_transfer,
2086                old_obj_ver.unwrap_or_default(),
2087                contents,
2088                protocol_config,
2089                Mode::packages_are_predefined(),
2090            )
2091        }
2092    }
2093
2094    fn unwrap_type_tag_load(
2095        protocol_config: &ProtocolConfig,
2096        ty: Result<Type, ExecutionError>,
2097    ) -> Result<Type, ExecutionError> {
2098        if ty.is_err() && !protocol_config.type_tags_in_object_runtime() {
2099            panic!("Failed to load a type tag from the object runtime -- this shouldn't happen")
2100        } else {
2101            ty
2102        }
2103    }
2104
2105    pub enum EitherError {
2106        CommandArgument(CommandArgumentError),
2107        Execution(ExecutionError),
2108    }
2109
2110    impl From<ExecutionError> for EitherError {
2111        fn from(e: ExecutionError) -> Self {
2112            EitherError::Execution(e)
2113        }
2114    }
2115
2116    impl From<CommandArgumentError> for EitherError {
2117        fn from(e: CommandArgumentError) -> Self {
2118            EitherError::CommandArgument(e)
2119        }
2120    }
2121
2122    impl EitherError {
2123        pub fn into_execution_error(self, command_index: usize) -> ExecutionError {
2124            match self {
2125                EitherError::CommandArgument(e) => command_argument_error(e, command_index),
2126                EitherError::Execution(e) => e,
2127            }
2128        }
2129    }
2130
2131    fn convert_vm_error<S: MoveResolver<Err = SuiError>>(
2132        error: VMError,
2133        vm: &MoveVM,
2134        state_view: &S,
2135        resolve_abort_location_to_package_id: bool,
2136    ) -> ExecutionError {
2137        crate::error::convert_vm_error_impl(
2138            error,
2139            &|id: &ModuleId| {
2140                if resolve_abort_location_to_package_id {
2141                    state_view.relocate(id).unwrap_or_else(|_| id.clone())
2142                } else {
2143                    id.clone()
2144                }
2145            },
2146            &|id, function| {
2147                vm.load_module(id, state_view).ok().map(|module| {
2148                    let fdef = module.function_def_at(function);
2149                    let fhandle = module.function_handle_at(fdef.function);
2150                    module.identifier_at(fhandle.name).to_string()
2151                })
2152            },
2153        )
2154    }
2155    /// Special case errors for type arguments to Move functions
2156    fn convert_type_argument_error<S: MoveResolver<Err = SuiError>>(
2157        idx: usize,
2158        error: VMError,
2159        vm: &MoveVM,
2160        state_view: &S,
2161        resolve_abort_location_to_package_id: bool,
2162    ) -> ExecutionError {
2163        use sui_types::execution_status::TypeArgumentError;
2164        match error.major_status() {
2165            StatusCode::NUMBER_OF_TYPE_ARGUMENTS_MISMATCH => {
2166                ExecutionErrorKind::TypeArityMismatch.into()
2167            }
2168            StatusCode::TYPE_RESOLUTION_FAILURE => ExecutionErrorKind::TypeArgumentError {
2169                argument_idx: idx as TypeParameterIndex,
2170                kind: TypeArgumentError::TypeNotFound,
2171            }
2172            .into(),
2173            StatusCode::CONSTRAINT_NOT_SATISFIED => ExecutionErrorKind::TypeArgumentError {
2174                argument_idx: idx as TypeParameterIndex,
2175                kind: TypeArgumentError::ConstraintNotSatisfied,
2176            }
2177            .into(),
2178            _ => convert_vm_error(error, vm, state_view, resolve_abort_location_to_package_id),
2179        }
2180    }
2181}