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