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