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