Skip to main content

iota_adapter_latest/programmable_transactions/
context.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2026 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5pub use checked::*;
6
7#[iota_macros::with_checked_arithmetic]
8mod checked {
9    use std::{
10        borrow::Borrow,
11        cell::RefCell,
12        collections::{BTreeMap, BTreeSet, HashMap},
13        rc::Rc,
14        sync::Arc,
15    };
16
17    use iota_move_natives::object_runtime::{
18        self, LoadedRuntimeObject, ObjectRuntime, RuntimeResults, get_all_uids, max_event_error,
19    };
20    use iota_protocol_config::ProtocolConfig;
21    use iota_sdk_types::{
22        Address, Argument, CommandArgumentError, Event, ObjectData, ObjectId, Owner,
23        SharedObjectReference, StructTag, TypeTag, move_package::MovePackage,
24    };
25    use iota_types::{
26        balance::Balance,
27        base_types::TxContext,
28        coin::Coin,
29        error::{ExecutionError, ExecutionErrorKind, command_argument_error},
30        execution::{ExecutionResults, ExecutionResultsV1},
31        iota_sdk_types_conversions::{
32            identifier_core_to_sdk, identifier_sdk_to_core, struct_tag_core_to_sdk,
33            type_tag_core_to_sdk,
34        },
35        metrics::LimitsMetrics,
36        move_package::{MovePackageExt, derive_package_metadata_id},
37        object::{MoveObject, MoveObjectExt, Object, ObjectInner},
38        storage::{BackingPackageStore, DenyListResult, PackageObject},
39        transaction::CallArg,
40    };
41    use move_binary_format::{
42        CompiledModule,
43        errors::{Location, PartialVMError, PartialVMResult, VMError, VMResult},
44        file_format::{CodeOffset, FunctionDefinitionIndex, TypeParameterIndex},
45    };
46    use move_core_types::{
47        account_address::AccountAddress, identifier::IdentStr, language_storage::ModuleId,
48        resolver::ModuleResolver, vm_status::StatusCode,
49    };
50    use move_trace_format::format::MoveTraceBuilder;
51    use move_vm_runtime::{
52        move_vm::MoveVM,
53        native_extensions::NativeContextExtensions,
54        session::{LoadedFunctionInstantiation, SerializedReturnValues},
55    };
56    use move_vm_types::{data_store::DataStore, loaded_data::runtime_types::Type};
57    use tracing::instrument;
58
59    use crate::{
60        adapter::new_native_extensions,
61        error::convert_vm_error,
62        execution_mode::ExecutionMode,
63        execution_value::{
64            CommandKind, ExecutionState, InputObjectMetadata, InputValue, ObjectContents,
65            ObjectValue, RawValueType, ResultValue, TryFromValue, UsageKind, Value,
66        },
67        gas_charger::GasCharger,
68        gas_meter::IotaGasMeter,
69        programmable_transactions::linkage_view::LinkageView,
70        type_resolver::TypeTagResolver,
71    };
72
73    /// Maintains all runtime state specific to programmable transactions
74    pub struct ExecutionContext<'vm, 'state, 'a> {
75        /// The protocol config
76        pub protocol_config: &'a ProtocolConfig,
77        /// Metrics for reporting exceeded limits
78        pub metrics: Arc<LimitsMetrics>,
79        /// The MoveVM
80        pub vm: &'vm MoveVM,
81        /// The LinkageView for this session
82        pub linkage_view: LinkageView<'state>,
83        pub native_extensions: NativeContextExtensions<'state>,
84        /// The global state, used for resolving packages
85        pub state_view: &'state dyn ExecutionState,
86        /// A shared transaction context, contains transaction digest
87        /// information and manages the creation of new object IDs
88        pub tx_context: Rc<RefCell<TxContext>>,
89        /// The gas charger used for metering
90        pub gas_charger: &'a mut GasCharger,
91        /// Additional transfers not from the Move runtime
92        additional_transfers: Vec<(/* new owner */ Owner, ObjectValue)>,
93        /// Newly published packages
94        new_packages: Vec<MovePackage>,
95        /// User events are claimed after each Move call
96        user_events: Vec<(ModuleId, StructTag, Vec<u8>)>,
97        // runtime data
98        /// The runtime value for the Gas coin, None if it has been taken/moved
99        gas: InputValue,
100        /// The runtime value for the inputs/call args, None if it has been
101        /// taken/moved
102        inputs: Vec<InputValue>,
103        /// The results of a given command. For most commands, the inner vector
104        /// will have length 1. It will only not be 1 for Move calls
105        /// with multiple return values. Inner values are None if
106        /// taken/moved by-value
107        results: Vec<Vec<ResultValue>>,
108        /// Map of arguments that are currently borrowed in this command, true
109        /// if the borrow is mutable This gets cleared out when new
110        /// results are pushed, i.e. the end of a command
111        borrowed: HashMap<Arg, /* mut */ bool>,
112    }
113
114    /// A write for an object that was generated outside of the Move
115    /// ObjectRuntime
116    struct AdditionalWrite {
117        /// The new owner of the object
118        recipient: Owner,
119        /// the type of the object,
120        type_: Type,
121        /// contents of the object
122        bytes: Vec<u8>,
123    }
124
125    #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
126    pub struct Arg(Arg_);
127
128    #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
129    enum Arg_ {
130        V1(Argument),
131        V2(NormalizedArg),
132    }
133
134    #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
135    enum NormalizedArg {
136        GasCoin,
137        Input(u16),
138        Result(u16, u16),
139    }
140
141    impl<'vm, 'state, 'a> ExecutionContext<'vm, 'state, 'a> {
142        /// Creates a new instance of the transaction execution context,
143        /// initializing the necessary components such as protocol
144        /// configuration, Move VM, gas management, inputs, and native
145        /// extensions. This function processes the input arguments, sets up gas
146        /// handling for the transaction, and prepares the state for
147        /// executing Move programs.
148        #[instrument(name = "ExecutionContext::new", level = "trace", skip_all)]
149        pub fn new(
150            protocol_config: &'a ProtocolConfig,
151            metrics: Arc<LimitsMetrics>,
152            vm: &'vm MoveVM,
153            state_view: &'state dyn ExecutionState,
154            tx_context: Rc<RefCell<TxContext>>,
155            gas_charger: &'a mut GasCharger,
156            inputs: Vec<CallArg>,
157        ) -> Result<Self, ExecutionError>
158        where
159            'a: 'state,
160        {
161            let mut linkage_view = LinkageView::new(Box::new(state_view.as_iota_resolver()));
162            let mut input_object_map = BTreeMap::new();
163            let inputs = inputs
164                .into_iter()
165                .map(|call_arg| {
166                    load_call_arg(
167                        vm,
168                        state_view,
169                        &mut linkage_view,
170                        &[],
171                        &mut input_object_map,
172                        call_arg,
173                    )
174                })
175                .collect::<Result<_, ExecutionError>>()?;
176            let gas = if let Some(gas_coin) = gas_charger.gas_coin() {
177                let mut gas = load_object(
178                    vm,
179                    state_view,
180                    &mut linkage_view,
181                    &[],
182                    &mut input_object_map,
183                    // imm override
184                    false,
185                    gas_coin,
186                )?;
187                // subtract the max gas budget. This amount is off limits in the programmable
188                // transaction, so to mimic this "off limits" behavior, we act
189                // as if the coin has less balance than it really does
190                let Some(Value::Object(ObjectValue {
191                    contents: ObjectContents::Coin(coin),
192                    ..
193                })) = &mut gas.inner.value
194                else {
195                    invariant_violation!("Gas object should be a populated coin")
196                };
197
198                let max_gas_in_balance = gas_charger.gas_budget();
199                let Some(new_balance) = coin.balance.value().checked_sub(max_gas_in_balance) else {
200                    invariant_violation!(
201                        "Transaction input checker should check that there is enough gas"
202                    );
203                };
204                coin.balance = Balance::new(new_balance);
205                gas
206            } else {
207                InputValue {
208                    object_metadata: None,
209                    inner: ResultValue {
210                        last_usage_kind: None,
211                        value: None,
212                    },
213                }
214            };
215            let native_extensions = new_native_extensions(
216                state_view.as_child_resolver(),
217                input_object_map,
218                !gas_charger.is_unmetered(),
219                protocol_config,
220                metrics.clone(),
221                tx_context.clone(),
222                state_view.read_auth_context(),
223            );
224
225            // Set the profiler if in CLI
226            #[skip_checked_arithmetic]
227            move_vm_profiler::tracing_feature_enabled! {
228                use move_vm_profiler::GasProfiler;
229                use move_vm_types::gas::GasMeter;
230
231                let ref_context: &RefCell<TxContext> = tx_context.borrow();
232                let tx_digest = ref_context.borrow().digest();
233                let remaining_gas: u64 =
234                    move_vm_types::gas::GasMeter::remaining_gas(&IotaGasMeter(gas_charger.move_gas_status_mut()))
235                        .into();
236                IotaGasMeter(gas_charger.move_gas_status_mut())
237                    .set_profiler(GasProfiler::init(
238                        &vm.config().profiler_config,
239                        format!("{tx_digest}"),
240                        remaining_gas,
241                    ));
242            }
243
244            Ok(Self {
245                protocol_config,
246                metrics,
247                vm,
248                linkage_view,
249                native_extensions,
250                state_view,
251                tx_context,
252                gas_charger,
253                gas,
254                inputs,
255                results: vec![],
256                additional_transfers: vec![],
257                new_packages: vec![],
258                user_events: vec![],
259                borrowed: HashMap::new(),
260            })
261        }
262
263        pub fn object_runtime(&self) -> Result<&ObjectRuntime<'_>, ExecutionError> {
264            self.native_extensions
265                .get::<ObjectRuntime>()
266                .map_err(|e| self.convert_vm_error(e.finish(Location::Undefined)))
267        }
268
269        /// Create a new ID and update the state
270        pub fn fresh_id(&mut self) -> Result<ObjectId, ExecutionError> {
271            let object_id = self.tx_context.borrow_mut().fresh_id();
272            self.record_new_uid(object_id)?;
273            Ok(object_id)
274        }
275
276        /// Record a newly-created UID in the object runtime.
277        pub(crate) fn record_new_uid(&mut self, object_id: ObjectId) -> Result<(), ExecutionError> {
278            self.native_extensions
279                .get_mut()
280                .and_then(|object_runtime: &mut ObjectRuntime| object_runtime.new_id(object_id))
281                .map_err(|e| self.convert_vm_error(e.finish(Location::Undefined)))?;
282            Ok(())
283        }
284
285        /// Create a new ID and update the state
286        pub(crate) fn package_derived_metadata_id(
287            &mut self,
288            package_storage_id: ObjectId,
289        ) -> Result<ObjectId, ExecutionError> {
290            let object_id = derive_package_metadata_id(package_storage_id);
291            self.record_new_uid(object_id)?;
292            Ok(object_id)
293        }
294
295        /// Delete an ID and update the state
296        pub fn delete_id(&mut self, object_id: ObjectId) -> Result<(), ExecutionError> {
297            self.native_extensions
298                .get_mut()
299                .and_then(|object_runtime: &mut ObjectRuntime| object_runtime.delete_id(object_id))
300                .map_err(|e| self.convert_vm_error(e.finish(Location::Undefined)))
301        }
302
303        /// Set the link context for the session from the linkage information in
304        /// the MovePackage found at `package_id`.  Returns the runtime
305        /// ID of the link context package on success.
306        pub fn set_link_context(
307            &mut self,
308            package_id: ObjectId,
309        ) -> Result<AccountAddress, ExecutionError> {
310            if self.linkage_view.has_linkage(package_id) {
311                // Setting same context again, can skip.
312                return Ok(self
313                    .linkage_view
314                    .original_package_id()
315                    .unwrap_or(AccountAddress::new(package_id.into_bytes())));
316            }
317
318            let package = package_for_linkage(&self.linkage_view, package_id)
319                .map_err(|e| self.convert_vm_error(e))?;
320
321            self.linkage_view.set_linkage(package.move_package())
322        }
323
324        /// Load a type using the context's current session.
325        pub fn load_type(&mut self, type_tag: &TypeTag) -> VMResult<Type> {
326            load_type(
327                self.vm,
328                &mut self.linkage_view,
329                &self.new_packages,
330                type_tag,
331            )
332        }
333
334        /// Load a type using the context's current session.
335        pub fn load_type_from_struct(&mut self, struct_tag: &StructTag) -> VMResult<Type> {
336            load_type_from_struct(
337                self.vm,
338                &mut self.linkage_view,
339                &self.new_packages,
340                struct_tag,
341            )
342        }
343
344        /// Takes the user events from the runtime and tags them with the Move
345        /// module of the function that was invoked for the command
346        pub fn take_user_events(
347            &mut self,
348            module_id: &ModuleId,
349            function: FunctionDefinitionIndex,
350            last_offset: CodeOffset,
351        ) -> Result<(), ExecutionError> {
352            let events = self
353                .native_extensions
354                .get_mut()
355                .map(|object_runtime: &mut ObjectRuntime| object_runtime.take_user_events())
356                .map_err(|e| self.convert_vm_error(e.finish(Location::Undefined)))?;
357            let num_events = self.user_events.len() + events.len();
358            let max_events = self.protocol_config.max_num_event_emit();
359            if num_events as u64 > max_events {
360                let err = max_event_error(max_events)
361                    .at_code_offset(function, last_offset)
362                    .finish(Location::Module(module_id.clone()));
363                return Err(self.convert_vm_error(err));
364            }
365            let new_events = events
366                .into_iter()
367                .map(|(ty, tag, value)| {
368                    let layout = self
369                        .vm
370                        .get_runtime()
371                        .type_to_type_layout(&ty)
372                        .map_err(|e| self.convert_vm_error(e))?;
373                    let Some(bytes) = value.simple_serialize(&layout) else {
374                        invariant_violation!("Failed to deserialize already serialized Move value");
375                    };
376                    Ok((module_id.clone(), struct_tag_core_to_sdk(&tag), bytes))
377                })
378                .collect::<Result<Vec<_>, ExecutionError>>()?;
379            self.user_events.extend(new_events);
380            Ok(())
381        }
382
383        /// Takes an iterator of arguments and flattens a Result into a
384        /// NestedResult if there is more than one result.
385        /// However, it is currently gated to 1 result, so this function is in
386        /// place for future changes. This is currently blocked by more
387        /// invasive work needed to update argument idx in errors
388        pub fn splat_args<Items: IntoIterator<Item = Argument>>(
389            &self,
390            start_idx: usize,
391            args: Items,
392        ) -> Result<Vec<Arg>, ExecutionError>
393        where
394            Items::IntoIter: ExactSizeIterator,
395        {
396            if !self.protocol_config.normalize_ptb_arguments() {
397                Ok(args.into_iter().map(|arg| Arg(Arg_::V1(arg))).collect())
398            } else {
399                let args = args.into_iter();
400                let _args_len = args.len();
401                let mut res = vec![];
402                for (arg_idx, arg) in args.enumerate() {
403                    self.splat_arg(&mut res, arg)
404                        .map_err(|e| e.into_execution_error(start_idx + arg_idx))?;
405                }
406                debug_assert_eq!(res.len(), _args_len);
407                Ok(res)
408            }
409        }
410
411        fn splat_arg(&self, res: &mut Vec<Arg>, arg: Argument) -> Result<(), EitherError> {
412            match arg {
413                Argument::Gas => res.push(Arg(Arg_::V2(NormalizedArg::GasCoin))),
414                Argument::Input(i) => {
415                    if i as usize >= self.inputs.len() {
416                        return Err(CommandArgumentError::IndexOutOfBounds { index: i }.into());
417                    }
418                    res.push(Arg(Arg_::V2(NormalizedArg::Input(i))))
419                }
420                Argument::NestedResult(i, j) => {
421                    let Some(command_result) = self.results.get(i as usize) else {
422                        return Err(CommandArgumentError::IndexOutOfBounds { index: i }.into());
423                    };
424                    if j as usize >= command_result.len() {
425                        return Err(CommandArgumentError::SecondaryIndexOutOfBounds {
426                            result: i,
427                            subresult: j,
428                        }
429                        .into());
430                    };
431                    res.push(Arg(Arg_::V2(NormalizedArg::Result(i, j))))
432                }
433                Argument::Result(i) => {
434                    let Some(result) = self.results.get(i as usize) else {
435                        return Err(CommandArgumentError::IndexOutOfBounds { index: i }.into());
436                    };
437                    let Ok(len): Result<u16, _> = result.len().try_into() else {
438                        invariant_violation!("Result of length greater than u16::MAX");
439                    };
440                    if len != 1 {
441                        // TODO protocol config to allow splatting of args
442                        return Err(CommandArgumentError::InvalidResultArity { result: i }.into());
443                    }
444                    res.extend((0..len).map(|j| Arg(Arg_::V2(NormalizedArg::Result(i, j)))))
445                }
446                _ => {
447                    unimplemented!("a new Argument enum variant was added and needs to be handled")
448                }
449            }
450            Ok(())
451        }
452
453        pub fn one_arg(
454            &self,
455            command_arg_idx: usize,
456            arg: Argument,
457        ) -> Result<Arg, ExecutionError> {
458            let args = self.splat_args(command_arg_idx, vec![arg])?;
459            let Ok([arg]): Result<[Arg; 1], _> = args.try_into() else {
460                return Err(command_argument_error(
461                    CommandArgumentError::InvalidArgumentArity,
462                    command_arg_idx,
463                ));
464            };
465            Ok(arg)
466        }
467
468        /// Registers `bytes` as an additional pure input value and returns the
469        /// [`Argument`] referring to it. This lets the adapter feed
470        /// synthesized arguments (values not present in the original
471        /// transaction inputs) into [`Self::splat_args`] and, in turn, into a
472        /// Move call. Pair a run of these calls with [`Self::num_inputs`] /
473        /// [`Self::truncate_inputs`] to drop the synthesized inputs afterwards.
474        pub(crate) fn add_pure_input(
475            &mut self,
476            bytes: Vec<u8>,
477        ) -> Result<Argument, ExecutionError> {
478            let Ok(index) = u16::try_from(self.inputs.len()) else {
479                invariant_violation!("too many inputs to register an additional pure input");
480            };
481            self.inputs
482                .push(InputValue::new_raw(RawValueType::Any, bytes));
483            Ok(Argument::Input(index))
484        }
485
486        /// The current number of registered inputs. Capture this before a run
487        /// of [`Self::add_pure_input`] calls and pass it to
488        /// [`Self::truncate_inputs`] afterwards to drop the synthesized inputs.
489        pub(crate) fn num_inputs(&self) -> usize {
490            self.inputs.len()
491        }
492
493        /// Drops every input registered at or past `len`, removing the pure
494        /// inputs added via [`Self::add_pure_input`] once they are no longer
495        /// needed. `len` must come from an earlier [`Self::num_inputs`] call.
496        pub(crate) fn truncate_inputs(&mut self, len: usize) {
497            self.inputs.truncate(len);
498        }
499
500        /// Get the argument value. Cloning the value if it is copyable, and
501        /// setting its value to None if it is not (making it
502        /// unavailable). Errors if out of bounds, if the argument is
503        /// borrowed, if it is unavailable (already taken), or if it is
504        /// an object that cannot be taken by value (shared or immutable)
505        pub fn by_value_arg<V: TryFromValue>(
506            &mut self,
507            command_kind: CommandKind,
508            arg_idx: usize,
509            arg: Arg,
510        ) -> Result<V, ExecutionError> {
511            self.by_value_arg_(command_kind, arg)
512                .map_err(|e| e.into_execution_error(arg_idx))
513        }
514        fn by_value_arg_<V: TryFromValue>(
515            &mut self,
516            command_kind: CommandKind,
517            arg: Arg,
518        ) -> Result<V, EitherError> {
519            let is_borrowed = self.arg_is_borrowed(&arg);
520            let (input_metadata_opt, val_opt) = self.borrow_mut(arg, UsageKind::ByValue)?;
521            let is_copyable = if let Some(val) = val_opt {
522                val.is_copyable()
523            } else {
524                return Err(CommandArgumentError::InvalidValueUsage.into());
525            };
526            // If it was taken, we catch this above.
527            // If it was not copyable and was borrowed, error as it creates a dangling
528            // reference in effect.
529            // We allow copyable values to be copied out even if borrowed, as we do not care
530            // about referential transparency at this level.
531            if !is_copyable && is_borrowed {
532                return Err(CommandArgumentError::InvalidValueUsage.into());
533            }
534            // Gas coin cannot be taken by value, except in TransferObjects
535            if arg.is_gas_coin() && !matches!(command_kind, CommandKind::TransferObjects) {
536                return Err(CommandArgumentError::InvalidGasCoinUsage.into());
537            }
538            // Immutable objects cannot be taken by value
539            if matches!(
540                input_metadata_opt,
541                Some(InputObjectMetadata::InputObject {
542                    owner: Owner::Immutable,
543                    ..
544                })
545            ) {
546                return Err(CommandArgumentError::InvalidObjectByValue.into());
547            }
548
549            // Any input object taken by value must be mutable
550            if matches!(
551                input_metadata_opt,
552                Some(InputObjectMetadata::InputObject {
553                    is_mutable_input: false,
554                    ..
555                })
556            ) {
557                return Err(CommandArgumentError::InvalidObjectByValue.into());
558            }
559
560            let val = if is_copyable {
561                val_opt.as_ref().unwrap().clone()
562            } else {
563                val_opt.take().unwrap()
564            };
565            Ok(V::try_from_value(val)?)
566        }
567
568        /// Mimic a mutable borrow by taking the argument value, setting its
569        /// value to None, making it unavailable. The value will be
570        /// marked as borrowed and must be returned with restore_arg
571        /// Errors if out of bounds, if the argument is borrowed, if it is
572        /// unavailable (already taken), or if it is an object that
573        /// cannot be mutably borrowed (immutable)
574        pub fn borrow_arg_mut<V: TryFromValue>(
575            &mut self,
576            arg_idx: usize,
577            arg: Arg,
578        ) -> Result<V, ExecutionError> {
579            self.borrow_arg_mut_(arg)
580                .map_err(|e| e.into_execution_error(arg_idx))
581        }
582        fn borrow_arg_mut_<V: TryFromValue>(&mut self, arg: Arg) -> Result<V, EitherError> {
583            // mutable borrowing requires unique usage
584            if self.arg_is_borrowed(&arg) {
585                return Err(CommandArgumentError::InvalidValueUsage.into());
586            }
587            self.borrowed.insert(arg, /* is_mut */ true);
588            let (input_metadata_opt, val_opt) = self.borrow_mut(arg, UsageKind::BorrowMut)?;
589            let is_copyable = if let Some(val) = val_opt {
590                val.is_copyable()
591            } else {
592                // error if taken
593                return Err(CommandArgumentError::InvalidValueUsage.into());
594            };
595            if let Some(InputObjectMetadata::InputObject {
596                is_mutable_input: false,
597                ..
598            }) = input_metadata_opt
599            {
600                return Err(CommandArgumentError::InvalidObjectByMutRef.into());
601            }
602            // if it is copyable, don't take it as we allow for the value to be copied even
603            // if mutably borrowed
604            let val = if is_copyable {
605                val_opt.as_ref().unwrap().clone()
606            } else {
607                val_opt.take().unwrap()
608            };
609            Ok(V::try_from_value(val)?)
610        }
611
612        /// Mimics an immutable borrow by cloning the argument value without
613        /// setting its value to None Errors if out of bounds, if the
614        /// argument is mutably borrowed, or if it is unavailable
615        /// (already taken)
616        pub fn borrow_arg<V: TryFromValue>(
617            &mut self,
618            arg_idx: usize,
619            arg: Arg,
620            type_: &Type,
621        ) -> Result<V, ExecutionError> {
622            self.borrow_arg_(arg, type_)
623                .map_err(|e| e.into_execution_error(arg_idx))
624        }
625        fn borrow_arg_<V: TryFromValue>(
626            &mut self,
627            arg: Arg,
628            arg_type: &Type,
629        ) -> Result<V, EitherError> {
630            // immutable borrowing requires the value was not mutably borrowed.
631            // If it was copied, that is okay.
632            // If it was taken/moved, we will find out below
633            if self.arg_is_mut_borrowed(&arg) {
634                return Err(CommandArgumentError::InvalidValueUsage.into());
635            }
636            self.borrowed.insert(arg, /* is_mut */ false);
637            let (_input_metadata_opt, val_opt) = self.borrow_mut(arg, UsageKind::BorrowImm)?;
638            if val_opt.is_none() {
639                return Err(CommandArgumentError::InvalidValueUsage.into());
640            }
641
642            // We eagerly reify receiving argument types at the first usage of them.
643            if let &mut Some(Value::Receiving(_, _, ref mut recv_arg_type @ None)) = val_opt {
644                let Type::Reference(inner) = arg_type else {
645                    return Err(CommandArgumentError::InvalidValueUsage.into());
646                };
647                *recv_arg_type = Some(*(*inner).clone());
648            }
649
650            Ok(V::try_from_value(val_opt.as_ref().unwrap().clone())?)
651        }
652
653        /// Restore an argument after being mutably borrowed
654        pub fn restore_arg<Mode: ExecutionMode>(
655            &mut self,
656            updates: &mut Mode::ArgumentUpdates,
657            arg: Arg,
658            value: Value,
659        ) -> Result<(), ExecutionError> {
660            Mode::add_argument_update(self, updates, arg.into(), &value)?;
661            let was_mut_opt = self.borrowed.remove(&arg);
662            assert_invariant!(
663                was_mut_opt.is_some() && was_mut_opt.unwrap(),
664                "Should never restore a non-mut borrowed value. \
665                The take+restore is an implementation detail of mutable references"
666            );
667            // restore is exclusively used for mut
668            let Ok((_, value_opt)) = self.borrow_mut_impl(arg, None) else {
669                invariant_violation!("Should be able to borrow argument to restore it")
670            };
671
672            let old_value = value_opt.replace(value);
673            assert_invariant!(
674                old_value.is_none() || old_value.unwrap().is_copyable(),
675                "Should never restore a non-taken value, unless it is copyable. \
676                The take+restore is an implementation detail of mutable references"
677            );
678
679            Ok(())
680        }
681
682        /// Transfer the object to a new owner
683        pub fn transfer_object(
684            &mut self,
685            obj: ObjectValue,
686            addr: Address,
687        ) -> Result<(), ExecutionError> {
688            self.additional_transfers.push((Owner::Address(addr), obj));
689            Ok(())
690        }
691
692        /// Freeze the object
693        pub fn freeze_object(&mut self, obj: ObjectValue) -> Result<(), ExecutionError> {
694            self.additional_transfers.push((Owner::Immutable, obj));
695            Ok(())
696        }
697
698        /// Create a new package
699        pub fn new_package<'p>(
700            &self,
701            modules: &[CompiledModule],
702            dependencies: impl IntoIterator<Item = &'p MovePackage>,
703        ) -> Result<MovePackage, ExecutionError> {
704            MovePackage::new_initial(modules, self.protocol_config, dependencies)
705        }
706
707        /// Create a package upgrade from `previous_package` with `new_modules`
708        /// and `dependencies`
709        pub fn upgrade_package<'p>(
710            &self,
711            storage_id: ObjectId,
712            previous_package: &MovePackage,
713            new_modules: &[CompiledModule],
714            dependencies: impl IntoIterator<Item = &'p MovePackage>,
715        ) -> Result<MovePackage, ExecutionError> {
716            previous_package.new_upgraded(
717                storage_id,
718                new_modules,
719                self.protocol_config,
720                dependencies,
721            )
722        }
723
724        /// Add a newly created package to write as an effect of the transaction
725        pub fn write_package(&mut self, package: MovePackage) {
726            self.new_packages.push(package);
727        }
728
729        /// Return the last package pushed in `write_package`.
730        /// This function should be used in block of codes that push a package,
731        /// verify it, run the init and in case of error will remove the
732        /// package. The package has to be pushed for the init to run
733        /// correctly.
734        pub fn pop_package(&mut self) -> Option<MovePackage> {
735            self.new_packages.pop()
736        }
737
738        /// Finish a command: clearing the borrows and adding the results to the
739        /// result vector
740        pub fn push_command_results(&mut self, results: Vec<Value>) -> Result<(), ExecutionError> {
741            assert_invariant!(
742                self.borrowed.values().all(|is_mut| !is_mut),
743                "all mut borrows should be restored"
744            );
745            // clear borrow state
746            self.borrowed = HashMap::new();
747            self.results
748                .push(results.into_iter().map(ResultValue::new).collect());
749            Ok(())
750        }
751
752        /// Determine the object changes and collect all user events
753        pub fn finish<Mode: ExecutionMode>(self) -> Result<ExecutionResults, ExecutionError> {
754            let Self {
755                protocol_config,
756                vm,
757                linkage_view,
758                mut native_extensions,
759                tx_context,
760                gas_charger,
761                additional_transfers,
762                new_packages,
763                gas,
764                inputs,
765                results,
766                user_events,
767                state_view,
768                ..
769            } = self;
770            let ref_context: &RefCell<TxContext> = tx_context.borrow();
771            let tx_digest = ref_context.borrow().digest();
772
773            let gas_id_opt = gas.object_metadata.as_ref().map(|info| info.id());
774            let mut loaded_runtime_objects = BTreeMap::new();
775            let mut additional_writes = BTreeMap::new();
776            let mut by_value_shared_objects = BTreeSet::new();
777            for input in inputs.into_iter().chain(std::iter::once(gas)) {
778                let InputValue {
779                    object_metadata:
780                        Some(InputObjectMetadata::InputObject {
781                            // We are only interested in mutable inputs.
782                            is_mutable_input: true,
783                            id,
784                            version,
785                            owner,
786                        }),
787                    inner: ResultValue { value, .. },
788                } = input
789                else {
790                    continue;
791                };
792                loaded_runtime_objects.insert(
793                    id,
794                    LoadedRuntimeObject {
795                        version,
796                        is_modified: true,
797                    },
798                );
799                if let Some(Value::Object(object_value)) = value {
800                    add_additional_write(&mut additional_writes, owner, object_value)?;
801                } else if owner.is_shared() {
802                    by_value_shared_objects.insert(id);
803                }
804            }
805            // check for unused values
806            // disable this check for dev inspect
807            if !Mode::allow_arbitrary_values() {
808                for (i, command_result) in results.iter().enumerate() {
809                    for (j, result_value) in command_result.iter().enumerate() {
810                        let ResultValue {
811                            last_usage_kind,
812                            value,
813                        } = result_value;
814                        match value {
815                            None => (),
816                            Some(Value::Object(_)) => {
817                                return Err(ExecutionErrorKind::UnusedValueWithoutDrop {
818                                    result: i as u16,
819                                    subresult: j as u16,
820                                }
821                                .into());
822                            }
823                            Some(Value::Raw(RawValueType::Any, _)) => (),
824                            Some(Value::Raw(RawValueType::Loaded { abilities, .. }, _)) => {
825                                // - nothing to check for drop
826                                // - if it does not have drop, but has copy, the last usage must be
827                                //   by value in order to "lie" and say that the last usage is
828                                //   actually a take instead of a clone
829                                // - Otherwise, an error
830                                if abilities.has_drop()
831                                    || (abilities.has_copy()
832                                        && matches!(last_usage_kind, Some(UsageKind::ByValue)))
833                                {
834                                } else {
835                                    let msg = if abilities.has_copy() {
836                                        "The value has copy, but not drop. \
837                                        Its last usage must be by-value so it can be taken."
838                                    } else {
839                                        "Unused value without drop"
840                                    };
841                                    return Err(ExecutionError::new_with_source(
842                                        ExecutionErrorKind::UnusedValueWithoutDrop {
843                                            result: i as u16,
844                                            subresult: j as u16,
845                                        },
846                                        msg,
847                                    ));
848                                }
849                            }
850                            // Receiving arguments can be dropped without being received
851                            Some(Value::Receiving(_, _, _)) => (),
852                        }
853                    }
854                }
855            }
856            // add transfers from TransferObjects command
857            for (owner, object_value) in additional_transfers {
858                add_additional_write(&mut additional_writes, owner, object_value)?;
859            }
860            // Refund unused gas
861            if let Some(gas_id) = gas_id_opt {
862                refund_max_gas_budget(&mut additional_writes, gas_charger, gas_id)?;
863            }
864
865            let object_runtime: ObjectRuntime = native_extensions
866                .remove()
867                .map_err(|e| convert_vm_error(e.finish(Location::Undefined), vm, &linkage_view))?;
868
869            let RuntimeResults {
870                writes,
871                user_events: remaining_events,
872                loaded_child_objects,
873                mut created_object_ids,
874                deleted_object_ids,
875            } = object_runtime.finish()?;
876            assert_invariant!(
877                remaining_events.is_empty(),
878                "Events should be taken after every Move call"
879            );
880
881            loaded_runtime_objects.extend(loaded_child_objects);
882
883            let mut written_objects = BTreeMap::new();
884            for package in new_packages {
885                let package_obj = Object::new_from_package(package, tx_digest);
886                let id = package_obj.id();
887                created_object_ids.insert(id);
888                written_objects.insert(id, package_obj);
889            }
890            for (id, additional_write) in additional_writes {
891                let AdditionalWrite {
892                    recipient,
893                    type_,
894                    bytes,
895                } = additional_write;
896
897                let move_object = {
898                    create_written_object(
899                        vm,
900                        &linkage_view,
901                        protocol_config,
902                        &loaded_runtime_objects,
903                        id,
904                        type_,
905                        bytes,
906                    )?
907                };
908                let object = Object::new_move(move_object, recipient, tx_digest);
909                written_objects.insert(id, object);
910                if let Some(loaded) = loaded_runtime_objects.get_mut(&id) {
911                    loaded.is_modified = true;
912                }
913            }
914
915            for (id, (recipient, ty, value)) in writes {
916                let layout = vm
917                    .get_runtime()
918                    .type_to_type_layout(&ty)
919                    .map_err(|e| convert_vm_error(e, vm, &linkage_view))?;
920                let Some(bytes) = value.simple_serialize(&layout) else {
921                    invariant_violation!("Failed to deserialize already serialized Move value");
922                };
923                let move_object = {
924                    create_written_object(
925                        vm,
926                        &linkage_view,
927                        protocol_config,
928                        &loaded_runtime_objects,
929                        id,
930                        ty,
931                        bytes,
932                    )?
933                };
934                let object = Object::new_move(move_object, recipient, tx_digest);
935                written_objects.insert(id, object);
936            }
937
938            // Before finishing, ensure that any shared object taken by value by the
939            // transaction is either:
940            // 1. Mutated (and still has a shared ownership); or
941            // 2. Deleted.
942            // Otherwise, the shared object operation is not allowed and we fail the
943            // transaction.
944            for id in &by_value_shared_objects {
945                // If it's been written it must have been reshared so must still have an
946                // ownership of `Shared`.
947                if let Some(obj) = written_objects.get(id) {
948                    if !obj.is_shared() {
949                        return Err(ExecutionError::new(
950                            ExecutionErrorKind::SharedObjectOperationNotAllowed,
951                            Some(
952                                format!(
953                                    "Shared object operation on {id} not allowed: \
954                                     cannot be frozen, transferred, or wrapped"
955                                )
956                                .into(),
957                            ),
958                        ));
959                    }
960                } else {
961                    // If it's not in the written objects, the object must have been deleted.
962                    // Otherwise it's an error.
963                    if !deleted_object_ids.contains(id) {
964                        return Err(ExecutionError::new(
965                            ExecutionErrorKind::SharedObjectOperationNotAllowed,
966                            Some(
967                                format!("Shared object operation on {id} not allowed: \
968                                         shared objects used by value must be re-shared if not deleted").into(),
969                            ),
970                        ));
971                    }
972                }
973            }
974
975            let DenyListResult {
976                result,
977                num_non_gas_coin_owners,
978            } = state_view.check_coin_deny_list(&written_objects);
979            gas_charger.charge_coin_transfers(protocol_config, num_non_gas_coin_owners)?;
980            result?;
981
982            let user_events = user_events
983                .into_iter()
984                .map(|(module_id, tag, contents)| {
985                    let package_id = ObjectId::new(module_id.address().into_bytes());
986                    let module = identifier_core_to_sdk(module_id.name());
987                    let sender = ref_context.borrow().sender();
988                    Event {
989                        package_id,
990                        module,
991                        sender,
992                        type_: tag,
993                        contents,
994                    }
995                })
996                .collect();
997
998            Ok(ExecutionResults::V1(ExecutionResultsV1 {
999                written_objects,
1000                modified_objects: loaded_runtime_objects
1001                    .into_iter()
1002                    .filter_map(|(id, loaded)| loaded.is_modified.then_some(id))
1003                    .collect(),
1004                created_object_ids: created_object_ids.into_iter().collect(),
1005                deleted_object_ids: deleted_object_ids.into_iter().collect(),
1006                user_events,
1007            }))
1008        }
1009
1010        /// Convert a VM Error to an execution one
1011        pub fn convert_vm_error(&self, error: VMError) -> ExecutionError {
1012            crate::error::convert_vm_error(error, self.vm, &self.linkage_view)
1013        }
1014
1015        /// Special case errors for type arguments to Move functions
1016        pub fn convert_type_argument_error(&self, idx: usize, error: VMError) -> ExecutionError {
1017            use iota_sdk_types::TypeArgumentError;
1018            use move_core_types::vm_status::StatusCode;
1019            match error.major_status() {
1020                StatusCode::NUMBER_OF_TYPE_ARGUMENTS_MISMATCH => {
1021                    ExecutionErrorKind::TypeArityMismatch.into()
1022                }
1023                StatusCode::TYPE_RESOLUTION_FAILURE => ExecutionErrorKind::TypeArgumentError {
1024                    type_argument: idx as TypeParameterIndex,
1025                    kind: TypeArgumentError::TypeNotFound,
1026                }
1027                .into(),
1028                StatusCode::CONSTRAINT_NOT_SATISFIED => ExecutionErrorKind::TypeArgumentError {
1029                    type_argument: idx as TypeParameterIndex,
1030                    kind: TypeArgumentError::ConstraintNotSatisfied,
1031                }
1032                .into(),
1033                _ => self.convert_vm_error(error),
1034            }
1035        }
1036
1037        /// Returns true if the value at the argument's location is borrowed,
1038        /// mutably or immutably
1039        fn arg_is_borrowed(&self, arg: &Arg) -> bool {
1040            self.borrowed.contains_key(arg)
1041        }
1042
1043        /// Returns true if the value at the argument's location is mutably
1044        /// borrowed
1045        fn arg_is_mut_borrowed(&self, arg: &Arg) -> bool {
1046            matches!(self.borrowed.get(arg), Some(/* mut */ true))
1047        }
1048
1049        /// Internal helper to borrow the value for an argument and update the
1050        /// most recent usage
1051        fn borrow_mut(
1052            &mut self,
1053            arg: Arg,
1054            usage: UsageKind,
1055        ) -> Result<(Option<&InputObjectMetadata>, &mut Option<Value>), EitherError> {
1056            self.borrow_mut_impl(arg, Some(usage))
1057        }
1058
1059        /// Internal helper to borrow the value for an argument
1060        /// Updates the most recent usage if specified
1061        fn borrow_mut_impl(
1062            &mut self,
1063            arg: Arg,
1064            update_last_usage: Option<UsageKind>,
1065        ) -> Result<(Option<&InputObjectMetadata>, &mut Option<Value>), EitherError> {
1066            match arg.0 {
1067                Arg_::V1(arg) => {
1068                    assert_invariant!(
1069                        !self.protocol_config.normalize_ptb_arguments(),
1070                        "Should not be using v1 args with normalized args"
1071                    );
1072                    Ok(self.borrow_mut_impl_v1(arg, update_last_usage)?)
1073                }
1074                Arg_::V2(arg) => {
1075                    assert_invariant!(
1076                        self.protocol_config.normalize_ptb_arguments(),
1077                        "Should be using only v2 args with normalized args"
1078                    );
1079                    Ok(self.borrow_mut_impl_v2(arg, update_last_usage)?)
1080                }
1081            }
1082        }
1083
1084        // v1 of borrow_mut_impl
1085        fn borrow_mut_impl_v1(
1086            &mut self,
1087            arg: Argument,
1088            update_last_usage: Option<UsageKind>,
1089        ) -> Result<(Option<&InputObjectMetadata>, &mut Option<Value>), CommandArgumentError>
1090        {
1091            let (metadata, result_value) = match arg {
1092                Argument::Gas => (self.gas.object_metadata.as_ref(), &mut self.gas.inner),
1093                Argument::Input(i) => {
1094                    let Some(input_value) = self.inputs.get_mut(i as usize) else {
1095                        return Err(CommandArgumentError::IndexOutOfBounds { index: i });
1096                    };
1097                    (input_value.object_metadata.as_ref(), &mut input_value.inner)
1098                }
1099                Argument::Result(i) => {
1100                    let Some(command_result) = self.results.get_mut(i as usize) else {
1101                        return Err(CommandArgumentError::IndexOutOfBounds { index: i });
1102                    };
1103                    if command_result.len() != 1 {
1104                        return Err(CommandArgumentError::InvalidResultArity { result: i });
1105                    }
1106                    (None, &mut command_result[0])
1107                }
1108                Argument::NestedResult(i, j) => {
1109                    let Some(command_result) = self.results.get_mut(i as usize) else {
1110                        return Err(CommandArgumentError::IndexOutOfBounds { index: i });
1111                    };
1112                    let Some(result_value) = command_result.get_mut(j as usize) else {
1113                        return Err(CommandArgumentError::SecondaryIndexOutOfBounds {
1114                            result: i,
1115                            subresult: j,
1116                        });
1117                    };
1118                    (None, result_value)
1119                }
1120                _ => {
1121                    unimplemented!("a new Argument enum variant was added and needs to be handled")
1122                }
1123            };
1124            if let Some(usage) = update_last_usage {
1125                result_value.last_usage_kind = Some(usage);
1126            }
1127            Ok((metadata, &mut result_value.value))
1128        }
1129
1130        // v2 of borrow_mut_impl
1131        fn borrow_mut_impl_v2(
1132            &mut self,
1133            arg: NormalizedArg,
1134            update_last_usage: Option<UsageKind>,
1135        ) -> Result<(Option<&InputObjectMetadata>, &mut Option<Value>), ExecutionError> {
1136            let (metadata, result_value) = match arg {
1137                NormalizedArg::GasCoin => (self.gas.object_metadata.as_ref(), &mut self.gas.inner),
1138                NormalizedArg::Input(i) => {
1139                    let input_value = self
1140                        .inputs
1141                        .get_mut(i as usize)
1142                        .ok_or_else(|| make_invariant_violation!("bounds already checked"))?;
1143                    (input_value.object_metadata.as_ref(), &mut input_value.inner)
1144                }
1145                NormalizedArg::Result(i, j) => {
1146                    let result_value = self
1147                        .results
1148                        .get_mut(i as usize)
1149                        .ok_or_else(|| make_invariant_violation!("bounds already checked"))?
1150                        .get_mut(j as usize)
1151                        .ok_or_else(|| make_invariant_violation!("bounds already checked"))?;
1152                    (None, result_value)
1153                }
1154            };
1155            if let Some(usage) = update_last_usage {
1156                result_value.last_usage_kind = Some(usage);
1157            }
1158            Ok((metadata, &mut result_value.value))
1159        }
1160
1161        /// Executes a Move function bypassing visibility checks, allowing the
1162        /// execution of private or protected functions. This method
1163        /// sets up the necessary gas status and data store, and then
1164        /// delegates the execution to the Move VM runtime.
1165        pub(crate) fn execute_function_bypass_visibility(
1166            &mut self,
1167            module: &ModuleId,
1168            function_name: &IdentStr,
1169            ty_args: Vec<Type>,
1170            args: Vec<impl Borrow<[u8]>>,
1171            tracer: &mut Option<MoveTraceBuilder>,
1172        ) -> VMResult<SerializedReturnValues> {
1173            let gas_status = self.gas_charger.move_gas_status_mut();
1174            let mut data_store = IotaDataStore::new(&self.linkage_view, &self.new_packages);
1175            self.vm.get_runtime().execute_function_bypass_visibility(
1176                module,
1177                function_name,
1178                ty_args,
1179                args,
1180                &mut data_store,
1181                &mut IotaGasMeter(gas_status),
1182                &mut self.native_extensions,
1183                tracer.as_mut(),
1184            )
1185        }
1186
1187        /// Loads a Move function from the specified module with the given type
1188        /// arguments without executing it. This function initializes
1189        /// the data store and delegates the loading process to the Move
1190        /// VM runtime.
1191        pub(crate) fn load_function(
1192            &mut self,
1193            module_id: &ModuleId,
1194            function_name: &IdentStr,
1195            type_arguments: &[Type],
1196        ) -> VMResult<LoadedFunctionInstantiation> {
1197            let mut data_store = IotaDataStore::new(&self.linkage_view, &self.new_packages);
1198            self.vm.get_runtime().load_function(
1199                module_id,
1200                function_name,
1201                type_arguments,
1202                &mut data_store,
1203            )
1204        }
1205
1206        /// Constructs an `ObjectValue` based on the provided Move object type,
1207        /// transferability, usage context, and byte contents. This
1208        /// function utilizes the protocol configuration, Move VM, and
1209        /// linkage view to properly interpret and instantiate the object.
1210        pub(crate) fn make_object_value(
1211            &mut self,
1212            type_: StructTag,
1213            used_in_non_entry_move_call: bool,
1214            contents: &[u8],
1215        ) -> Result<ObjectValue, ExecutionError> {
1216            make_object_value(
1217                self.vm,
1218                &mut self.linkage_view,
1219                &self.new_packages,
1220                type_,
1221                used_in_non_entry_move_call,
1222                contents,
1223            )
1224        }
1225
1226        /// Publishes a bundle of Move modules to the blockchain under the
1227        /// specified sender's account address. The function initializes
1228        /// a data store and delegates the publishing operation to the Move VM
1229        /// runtime.
1230        pub fn publish_module_bundle(
1231            &mut self,
1232            modules: Vec<Vec<u8>>,
1233            sender: AccountAddress,
1234        ) -> VMResult<()> {
1235            // TODO: publish_module_bundle() currently doesn't charge gas.
1236            // Do we want to charge there?
1237            let mut data_store = IotaDataStore::new(&self.linkage_view, &self.new_packages);
1238            self.vm.get_runtime().publish_module_bundle(
1239                modules,
1240                sender,
1241                &mut data_store,
1242                &mut IotaGasMeter(self.gas_charger.move_gas_status_mut()),
1243            )
1244        }
1245    }
1246
1247    impl TypeTagResolver for ExecutionContext<'_, '_, '_> {
1248        /// Retrieves the `TypeTag` corresponding to the provided `Type` by
1249        /// querying the Move VM runtime.
1250        fn get_type_tag(&self, type_: &Type) -> Result<TypeTag, ExecutionError> {
1251            self.vm
1252                .get_runtime()
1253                .get_type_tag(type_)
1254                .map_err(|e| self.convert_vm_error(e))
1255                .map(|tt| type_tag_core_to_sdk(&tt))
1256        }
1257    }
1258
1259    /// Fetch the package at `package_id` with a view to using it as a link
1260    /// context.  Produces an error if the object at that ID does not exist,
1261    /// or is not a package.
1262    fn package_for_linkage(
1263        linkage_view: &LinkageView,
1264        package_id: ObjectId,
1265    ) -> VMResult<PackageObject> {
1266        use move_binary_format::errors::PartialVMError;
1267        use move_core_types::vm_status::StatusCode;
1268
1269        match linkage_view.get_package_object(&package_id) {
1270            Ok(Some(package)) => Ok(package),
1271            Ok(None) => Err(PartialVMError::new(StatusCode::LINKER_ERROR)
1272                .with_message(format!("Cannot find link context {package_id} in store"))
1273                .finish(Location::Undefined)),
1274            Err(err) => Err(PartialVMError::new(StatusCode::LINKER_ERROR)
1275                .with_message(format!(
1276                    "Error loading link context {package_id} from store: {err}"
1277                ))
1278                .finish(Location::Undefined)),
1279        }
1280    }
1281
1282    /// Loads a `Type` from the given `StructTag`, retrieving the corresponding
1283    /// struct from the package in storage. The function sets up the linkage
1284    /// context to resolve the struct's module and verifies
1285    /// any type parameter constraints. If the struct has type parameters, they
1286    /// are recursively loaded and verified.
1287    pub fn load_type_from_struct(
1288        vm: &MoveVM,
1289        linkage_view: &mut LinkageView,
1290        new_packages: &[MovePackage],
1291        struct_tag: &StructTag,
1292    ) -> VMResult<Type> {
1293        fn verification_error<T>(code: StatusCode) -> VMResult<T> {
1294            Err(PartialVMError::new(code).finish(Location::Undefined))
1295        }
1296
1297        // Load the package that the struct is defined in, in storage
1298        let defining_id = struct_tag.address().into();
1299        let package = package_for_linkage(linkage_view, defining_id)?;
1300
1301        // Set the defining package as the link context while loading the
1302        // struct
1303        let original_address = linkage_view
1304            .set_linkage(package.move_package())
1305            .map_err(|e| {
1306                PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR)
1307                    .with_message(e.to_string())
1308                    .finish(Location::Undefined)
1309            })?;
1310
1311        let runtime_id = ModuleId::new(
1312            original_address,
1313            identifier_sdk_to_core(struct_tag.module()),
1314        );
1315        let data_store = IotaDataStore::new(linkage_view, new_packages);
1316        let res = vm.get_runtime().load_type(
1317            &runtime_id,
1318            IdentStr::new(struct_tag.name().as_str()).unwrap(),
1319            &data_store,
1320        );
1321        linkage_view.reset_linkage();
1322        let (idx, struct_type) = res?;
1323
1324        // Recursively load type parameters, if necessary
1325        let type_param_constraints = struct_type.type_param_constraints();
1326        if type_param_constraints.len() != struct_tag.type_params().len() {
1327            return verification_error(StatusCode::NUMBER_OF_TYPE_ARGUMENTS_MISMATCH);
1328        }
1329
1330        if struct_tag.type_params().is_empty() {
1331            Ok(Type::Datatype(idx))
1332        } else {
1333            let loaded_type_params = struct_tag
1334                .type_params()
1335                .iter()
1336                .map(|type_param| load_type(vm, linkage_view, new_packages, type_param))
1337                .collect::<VMResult<Vec<_>>>()?;
1338
1339            // Verify that the type parameter constraints on the struct are met
1340            for (constraint, param) in type_param_constraints.zip(&loaded_type_params) {
1341                let abilities = vm.get_runtime().get_type_abilities(param)?;
1342                if !constraint.is_subset(abilities) {
1343                    return verification_error(StatusCode::CONSTRAINT_NOT_SATISFIED);
1344                }
1345            }
1346
1347            Ok(Type::DatatypeInstantiation(Box::new((
1348                idx,
1349                loaded_type_params,
1350            ))))
1351        }
1352    }
1353
1354    /// Load `type_tag` to get a `Type` in the provided `session`.  `session`'s
1355    /// linkage context may be reset after this operation, because during
1356    /// the operation, it may change when loading a struct.
1357    pub fn load_type(
1358        vm: &MoveVM,
1359        linkage_view: &mut LinkageView,
1360        new_packages: &[MovePackage],
1361        type_tag: &TypeTag,
1362    ) -> VMResult<Type> {
1363        Ok(match type_tag {
1364            TypeTag::Bool => Type::Bool,
1365            TypeTag::U8 => Type::U8,
1366            TypeTag::U16 => Type::U16,
1367            TypeTag::U32 => Type::U32,
1368            TypeTag::U64 => Type::U64,
1369            TypeTag::U128 => Type::U128,
1370            TypeTag::U256 => Type::U256,
1371            TypeTag::Address => Type::Address,
1372            TypeTag::Signer => Type::Signer,
1373
1374            TypeTag::Vector(inner) => {
1375                Type::Vector(Box::new(load_type(vm, linkage_view, new_packages, inner)?))
1376            }
1377            TypeTag::Struct(struct_tag) => {
1378                return load_type_from_struct(vm, linkage_view, new_packages, struct_tag);
1379            }
1380        })
1381    }
1382
1383    /// Constructs an `ObjectValue` based on the provided `StructTag`,
1384    /// contents, and additional flags such as transferability and usage
1385    /// context. If the object is a coin, it deserializes the contents into
1386    /// a `Coin` type; otherwise, it treats the contents as raw data. The
1387    /// function then loads the corresponding struct type from the Move
1388    /// package and verifies its abilities if needed.
1389    pub(crate) fn make_object_value(
1390        vm: &MoveVM,
1391        linkage_view: &mut LinkageView,
1392        new_packages: &[MovePackage],
1393        type_: StructTag,
1394        used_in_non_entry_move_call: bool,
1395        contents: &[u8],
1396    ) -> Result<ObjectValue, ExecutionError> {
1397        let contents = if type_.is_coin() {
1398            let Ok(coin) = Coin::from_bcs_bytes(contents) else {
1399                invariant_violation!("Could not deserialize a coin")
1400            };
1401            ObjectContents::Coin(coin)
1402        } else {
1403            ObjectContents::Raw(contents.to_vec())
1404        };
1405
1406        let type_ = load_type_from_struct(vm, linkage_view, new_packages, &type_)
1407            .map_err(|e| crate::error::convert_vm_error(e, vm, linkage_view))?;
1408        let abilities = vm
1409            .get_runtime()
1410            .get_type_abilities(&type_)
1411            .map_err(|e| crate::error::convert_vm_error(e, vm, linkage_view))?;
1412        let has_public_transfer = abilities.has_store();
1413        Ok(ObjectValue {
1414            type_,
1415            has_public_transfer,
1416            used_in_non_entry_move_call,
1417            contents,
1418        })
1419    }
1420
1421    impl Arg {
1422        fn is_gas_coin(&self) -> bool {
1423            // kept as two separate matches for exhaustiveness
1424            match self {
1425                Arg(Arg_::V1(a)) => matches!(a, Argument::Gas),
1426                Arg(Arg_::V2(n)) => matches!(n, NormalizedArg::GasCoin),
1427            }
1428        }
1429    }
1430
1431    impl From<Arg> for Argument {
1432        fn from(arg: Arg) -> Self {
1433            match arg.0 {
1434                Arg_::V1(a) => a,
1435                Arg_::V2(normalized) => match normalized {
1436                    NormalizedArg::GasCoin => Argument::Gas,
1437                    NormalizedArg::Input(i) => Argument::Input(i),
1438                    NormalizedArg::Result(i, j) => Argument::NestedResult(i, j),
1439                },
1440            }
1441        }
1442    }
1443
1444    /// Converts a provided `Object` into an `ObjectValue`, extracting and
1445    /// validating the `StructTag` and contents. This function assumes
1446    /// the object contains Move-specific data and passes the extracted data
1447    /// through `make_object_value` to create the corresponding `ObjectValue`.
1448    pub(crate) fn value_from_object(
1449        vm: &MoveVM,
1450        linkage_view: &mut LinkageView,
1451        new_packages: &[MovePackage],
1452        object: &Object,
1453    ) -> Result<ObjectValue, ExecutionError> {
1454        let ObjectInner {
1455            data: ObjectData::Struct(object),
1456            ..
1457        } = object.as_inner()
1458        else {
1459            invariant_violation!("Expected a Move object");
1460        };
1461
1462        let used_in_non_entry_move_call = false;
1463        make_object_value(
1464            vm,
1465            linkage_view,
1466            new_packages,
1467            object.struct_tag().clone(),
1468            used_in_non_entry_move_call,
1469            object.contents(),
1470        )
1471    }
1472
1473    /// Load an input object from the state_view
1474    fn load_object(
1475        vm: &MoveVM,
1476        state_view: &dyn ExecutionState,
1477        linkage_view: &mut LinkageView,
1478        new_packages: &[MovePackage],
1479        input_object_map: &mut BTreeMap<ObjectId, object_runtime::InputObject>,
1480        override_as_immutable: bool,
1481        id: ObjectId,
1482    ) -> Result<InputValue, ExecutionError> {
1483        let Some(obj) = state_view.read_object(&id) else {
1484            // protected by transaction input checker
1485            invariant_violation!("Object {} does not exist yet", id);
1486        };
1487        // override_as_immutable ==> Owner::Shared
1488        assert_invariant!(
1489            !override_as_immutable || matches!(obj.owner, Owner::Shared(_)),
1490            "override_as_immutable should only be set for shared objects"
1491        );
1492        let is_mutable_input = match obj.owner {
1493            Owner::Address(_) => true,
1494            Owner::Shared(_) => !override_as_immutable,
1495            Owner::Immutable => false,
1496            Owner::Object(_) => {
1497                // protected by transaction input checker
1498                invariant_violation!("Object-owned objects cannot be inputs")
1499            }
1500            _ => unimplemented!("a new Owner enum variant was added and needs to be handled"),
1501        };
1502        let owner = obj.owner;
1503        let version = obj.version();
1504        let object_metadata = InputObjectMetadata::InputObject {
1505            id,
1506            is_mutable_input,
1507            owner,
1508            version,
1509        };
1510        let obj_value = value_from_object(vm, linkage_view, new_packages, obj)?;
1511        let contained_uids = {
1512            let fully_annotated_layout = vm
1513                .get_runtime()
1514                .type_to_fully_annotated_layout(&obj_value.type_)
1515                .map_err(|e| convert_vm_error(e, vm, linkage_view))?;
1516            let mut bytes = vec![];
1517            obj_value.write_bcs_bytes(&mut bytes, None)?;
1518            match get_all_uids(&fully_annotated_layout, &bytes) {
1519                Err(e) => {
1520                    invariant_violation!("Unable to retrieve UIDs for object. Got error: {e}")
1521                }
1522                Ok(uids) => uids,
1523            }
1524        };
1525        let runtime_input = object_runtime::InputObject {
1526            contained_uids,
1527            owner,
1528            version,
1529        };
1530        let prev = input_object_map.insert(id, runtime_input);
1531        // protected by transaction input checker
1532        assert_invariant!(prev.is_none(), "Duplicate input object {}", id);
1533        Ok(InputValue::new_object(object_metadata, obj_value))
1534    }
1535
1536    /// Load an a CallArg, either an object or a raw set of BCS bytes
1537    fn load_call_arg(
1538        vm: &MoveVM,
1539        state_view: &dyn ExecutionState,
1540        linkage_view: &mut LinkageView,
1541        new_packages: &[MovePackage],
1542        input_object_map: &mut BTreeMap<ObjectId, object_runtime::InputObject>,
1543        call_arg: CallArg,
1544    ) -> Result<InputValue, ExecutionError> {
1545        Ok(match call_arg {
1546            CallArg::Pure(value) => InputValue::new_raw(RawValueType::Any, value),
1547            other => load_object_arg(
1548                vm,
1549                state_view,
1550                linkage_view,
1551                new_packages,
1552                input_object_map,
1553                other,
1554            )?,
1555        })
1556    }
1557
1558    /// Load an object `CallArg` from state view, marking if it can be treated
1559    /// as mutable or not
1560    fn load_object_arg(
1561        vm: &MoveVM,
1562        state_view: &dyn ExecutionState,
1563        linkage_view: &mut LinkageView,
1564        new_packages: &[MovePackage],
1565        input_object_map: &mut BTreeMap<ObjectId, object_runtime::InputObject>,
1566        obj_arg: CallArg,
1567    ) -> Result<InputValue, ExecutionError> {
1568        match obj_arg {
1569            CallArg::ImmutableOrOwned(object_ref) => load_object(
1570                vm,
1571                state_view,
1572                linkage_view,
1573                new_packages,
1574                input_object_map,
1575                // imm override
1576                false,
1577                object_ref.object_id,
1578            ),
1579            CallArg::Shared(SharedObjectReference {
1580                object_id: id,
1581                mutable,
1582                ..
1583            }) => load_object(
1584                vm,
1585                state_view,
1586                linkage_view,
1587                new_packages,
1588                input_object_map,
1589                // imm override
1590                !mutable,
1591                id,
1592            ),
1593            CallArg::Receiving(object_ref) => Ok(InputValue::new_receiving_object(
1594                object_ref.object_id,
1595                object_ref.version,
1596            )),
1597            CallArg::Pure(_) => Err(ExecutionError::invariant_violation(
1598                "unexpected pure CallArg in load_object_arg",
1599            )),
1600            _ => Err(ExecutionError::invariant_violation(
1601                "a new CallArg enum variant was added and needs to be handled",
1602            )),
1603        }
1604    }
1605
1606    /// Generate an additional write for an ObjectValue
1607    fn add_additional_write(
1608        additional_writes: &mut BTreeMap<ObjectId, AdditionalWrite>,
1609        owner: Owner,
1610        object_value: ObjectValue,
1611    ) -> Result<(), ExecutionError> {
1612        let ObjectValue {
1613            type_, contents, ..
1614        } = object_value;
1615        let bytes = match contents {
1616            ObjectContents::Coin(coin) => coin.to_bcs_bytes(),
1617            ObjectContents::Raw(bytes) => bytes,
1618        };
1619        let object_id =
1620            ObjectId::from_bytes(bytes.get(..ObjectId::LENGTH).ok_or_else(|| {
1621                ExecutionError::invariant_violation("No id for Raw object bytes")
1622            })?)
1623            .expect("ObjectId::LENGTH bytes is always a valid ObjectId");
1624        let additional_write = AdditionalWrite {
1625            recipient: owner,
1626            type_,
1627            bytes,
1628        };
1629        additional_writes.insert(object_id, additional_write);
1630        Ok(())
1631    }
1632
1633    /// The max budget was deducted from the gas coin at the beginning of the
1634    /// transaction, now we return exactly that amount. Gas will be charged
1635    /// by the execution engine
1636    fn refund_max_gas_budget(
1637        additional_writes: &mut BTreeMap<ObjectId, AdditionalWrite>,
1638        gas_charger: &mut GasCharger,
1639        gas_id: ObjectId,
1640    ) -> Result<(), ExecutionError> {
1641        let Some(AdditionalWrite { bytes, .. }) = additional_writes.get_mut(&gas_id) else {
1642            invariant_violation!("Gas object cannot be wrapped or destroyed")
1643        };
1644        let Ok(mut coin) = Coin::from_bcs_bytes(bytes) else {
1645            invariant_violation!("Gas object must be a coin")
1646        };
1647        let Some(new_balance) = coin.balance.value().checked_add(gas_charger.gas_budget()) else {
1648            return Err(ExecutionError::new_with_source(
1649                ExecutionErrorKind::CoinBalanceOverflow,
1650                "Gas coin too large after returning the max gas budget",
1651            ));
1652        };
1653        coin.balance = Balance::new(new_balance);
1654        *bytes = coin.to_bcs_bytes();
1655        Ok(())
1656    }
1657
1658    /// Generate an MoveObject given an updated/written object
1659    fn create_written_object(
1660        vm: &MoveVM,
1661        linkage_view: &LinkageView,
1662        protocol_config: &ProtocolConfig,
1663        objects_modified_at: &BTreeMap<ObjectId, LoadedRuntimeObject>,
1664        id: ObjectId,
1665        type_: Type,
1666        contents: Vec<u8>,
1667    ) -> Result<MoveObject, ExecutionError> {
1668        debug_assert_eq!(
1669            id,
1670            ObjectId::from_bytes(&contents[..ObjectId::LENGTH])
1671                .expect("object contents should start with an id")
1672        );
1673        let old_obj_ver = objects_modified_at
1674            .get(&id)
1675            .map(|obj: &LoadedRuntimeObject| obj.version);
1676
1677        let type_tag = type_tag_core_to_sdk(
1678            &vm.get_runtime()
1679                .get_type_tag(&type_)
1680                .map_err(|e| crate::error::convert_vm_error(e, vm, linkage_view))?,
1681        );
1682
1683        let struct_tag = match type_tag {
1684            TypeTag::Struct(inner) => *inner,
1685            _ => invariant_violation!("Non struct type for object"),
1686        };
1687        MoveObject::new_from_execution(
1688            struct_tag,
1689            old_obj_ver.unwrap_or_default(),
1690            contents,
1691            protocol_config,
1692        )
1693    }
1694
1695    // Implementation of the `DataStore` trait for the Move VM.
1696    // When used during execution it may have a list of new packages that have
1697    // just been published in the current context. Those are used for module/type
1698    // resolution when executing module init.
1699    // It may be created with an empty slice of packages either when no
1700    // publish/upgrade are performed or when a type is requested not during
1701    // execution.
1702    pub(crate) struct IotaDataStore<'state, 'a> {
1703        linkage_view: &'a LinkageView<'state>,
1704        new_packages: &'a [MovePackage],
1705    }
1706
1707    impl<'state, 'a> IotaDataStore<'state, 'a> {
1708        pub(crate) fn new(
1709            linkage_view: &'a LinkageView<'state>,
1710            new_packages: &'a [MovePackage],
1711        ) -> Self {
1712            Self {
1713                linkage_view,
1714                new_packages,
1715            }
1716        }
1717
1718        fn get_module(&self, module_id: &ModuleId) -> Option<&Vec<u8>> {
1719            for package in self.new_packages {
1720                if package.id != ObjectId::from(module_id.address().into_bytes()) {
1721                    continue;
1722                }
1723
1724                let module = package.get_module(&identifier_core_to_sdk(module_id.name()));
1725
1726                if module.is_some() {
1727                    return module;
1728                }
1729            }
1730            None
1731        }
1732    }
1733
1734    // TODO: `DataStore` will be reworked and this is likely to disappear.
1735    //       Leaving this comment around until then as testament to better days to
1736    // come...
1737    impl DataStore for IotaDataStore<'_, '_> {
1738        fn link_context(&self) -> AccountAddress {
1739            self.linkage_view.link_context()
1740        }
1741
1742        fn relocate(&self, module_id: &ModuleId) -> PartialVMResult<ModuleId> {
1743            self.linkage_view.relocate(module_id).map_err(|err| {
1744                PartialVMError::new(StatusCode::LINKER_ERROR)
1745                    .with_message(format!("Error relocating {module_id}: {err:?}"))
1746            })
1747        }
1748
1749        fn defining_module(
1750            &self,
1751            runtime_id: &ModuleId,
1752            struct_: &IdentStr,
1753        ) -> PartialVMResult<ModuleId> {
1754            self.linkage_view
1755                .defining_module(runtime_id, struct_)
1756                .map_err(|err| {
1757                    PartialVMError::new(StatusCode::LINKER_ERROR).with_message(format!(
1758                        "Error finding defining module for {runtime_id}::{struct_}: {err:?}"
1759                    ))
1760                })
1761        }
1762
1763        fn load_module(&self, module_id: &ModuleId) -> VMResult<Vec<u8>> {
1764            if let Some(bytes) = self.get_module(module_id) {
1765                return Ok(bytes.clone());
1766            }
1767            match self.linkage_view.get_module(module_id) {
1768                Ok(Some(bytes)) => Ok(bytes),
1769                Ok(None) => Err(PartialVMError::new(StatusCode::LINKER_ERROR)
1770                    .with_message(format!("Cannot find {module_id:?} in data cache"))
1771                    .finish(Location::Undefined)),
1772                Err(err) => {
1773                    let msg = format!("Unexpected storage error: {err:?}");
1774                    Err(
1775                        PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR)
1776                            .with_message(msg)
1777                            .finish(Location::Undefined),
1778                    )
1779                }
1780            }
1781        }
1782
1783        fn publish_module(&mut self, _module_id: &ModuleId, _blob: Vec<u8>) -> VMResult<()> {
1784            // we cannot panic here because during execution and publishing this is
1785            // currently called from the publish flow in the Move runtime
1786            Ok(())
1787        }
1788    }
1789
1790    enum EitherError {
1791        CommandArgument(CommandArgumentError),
1792        Execution(ExecutionError),
1793    }
1794
1795    impl From<ExecutionError> for EitherError {
1796        fn from(e: ExecutionError) -> Self {
1797            EitherError::Execution(e)
1798        }
1799    }
1800
1801    impl From<CommandArgumentError> for EitherError {
1802        fn from(e: CommandArgumentError) -> Self {
1803            EitherError::CommandArgument(e)
1804        }
1805    }
1806
1807    impl EitherError {
1808        fn into_execution_error(self, command_index: usize) -> ExecutionError {
1809            match self {
1810                EitherError::CommandArgument(e) => command_argument_error(e, command_index),
1811                EitherError::Execution(e) => e,
1812            }
1813        }
1814    }
1815}