iota_adapter_latest/programmable_transactions/
context.rs

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