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