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 {} not allowed: \
794                                     cannot be frozen, transferred, or wrapped",
795                                    id
796                                )
797                                .into(),
798                            ),
799                        ));
800                    }
801                } else {
802                    // If it's not in the written objects, the object must have been deleted.
803                    // Otherwise it's an error.
804                    if !deleted_object_ids.contains(id) {
805                        return Err(ExecutionError::new(
806                            ExecutionErrorKind::SharedObjectOperationNotAllowed,
807                            Some(
808                                format!("Shared object operation on {} not allowed: \
809                                         shared objects used by value must be re-shared if not deleted", id).into(),
810                            ),
811                        ));
812                    }
813                }
814            }
815
816            let DenyListResult {
817                result,
818                num_non_gas_coin_owners,
819            } = state_view.check_coin_deny_list(&written_objects);
820            gas_charger.charge_coin_transfers(protocol_config, num_non_gas_coin_owners)?;
821            result?;
822
823            let user_events = user_events
824                .into_iter()
825                .map(|(module_id, tag, contents)| {
826                    Event::new(
827                        module_id.address(),
828                        module_id.name(),
829                        tx_context.sender(),
830                        tag,
831                        contents,
832                    )
833                })
834                .collect();
835
836            Ok(ExecutionResults::V1(ExecutionResultsV1 {
837                written_objects,
838                modified_objects: loaded_runtime_objects
839                    .into_iter()
840                    .filter_map(|(id, loaded)| loaded.is_modified.then_some(id))
841                    .collect(),
842                created_object_ids: created_object_ids.into_iter().collect(),
843                deleted_object_ids: deleted_object_ids.into_iter().collect(),
844                user_events,
845            }))
846        }
847
848        /// Convert a VM Error to an execution one
849        pub fn convert_vm_error(&self, error: VMError) -> ExecutionError {
850            crate::error::convert_vm_error(error, self.vm, &self.linkage_view)
851        }
852
853        /// Special case errors for type arguments to Move functions
854        pub fn convert_type_argument_error(&self, idx: usize, error: VMError) -> ExecutionError {
855            use iota_types::execution_status::TypeArgumentError;
856            use move_core_types::vm_status::StatusCode;
857            match error.major_status() {
858                StatusCode::NUMBER_OF_TYPE_ARGUMENTS_MISMATCH => {
859                    ExecutionErrorKind::TypeArityMismatch.into()
860                }
861                StatusCode::TYPE_RESOLUTION_FAILURE => ExecutionErrorKind::TypeArgumentError {
862                    argument_idx: idx as TypeParameterIndex,
863                    kind: TypeArgumentError::TypeNotFound,
864                }
865                .into(),
866                StatusCode::CONSTRAINT_NOT_SATISFIED => ExecutionErrorKind::TypeArgumentError {
867                    argument_idx: idx as TypeParameterIndex,
868                    kind: TypeArgumentError::ConstraintNotSatisfied,
869                }
870                .into(),
871                _ => self.convert_vm_error(error),
872            }
873        }
874
875        /// Returns true if the value at the argument's location is borrowed,
876        /// mutably or immutably
877        fn arg_is_borrowed(&self, arg: &Argument) -> bool {
878            self.borrowed.contains_key(arg)
879        }
880
881        /// Returns true if the value at the argument's location is mutably
882        /// borrowed
883        fn arg_is_mut_borrowed(&self, arg: &Argument) -> bool {
884            matches!(self.borrowed.get(arg), Some(/* mut */ true))
885        }
886
887        /// Internal helper to borrow the value for an argument and update the
888        /// most recent usage
889        fn borrow_mut(
890            &mut self,
891            arg: Argument,
892            usage: UsageKind,
893        ) -> Result<(Option<&InputObjectMetadata>, &mut Option<Value>), CommandArgumentError>
894        {
895            self.borrow_mut_impl(arg, Some(usage))
896        }
897
898        /// Internal helper to borrow the value for an argument
899        /// Updates the most recent usage if specified
900        fn borrow_mut_impl(
901            &mut self,
902            arg: Argument,
903            update_last_usage: Option<UsageKind>,
904        ) -> Result<(Option<&InputObjectMetadata>, &mut Option<Value>), CommandArgumentError>
905        {
906            let (metadata, result_value) = match arg {
907                Argument::GasCoin => (self.gas.object_metadata.as_ref(), &mut self.gas.inner),
908                Argument::Input(i) => {
909                    let Some(input_value) = self.inputs.get_mut(i as usize) else {
910                        return Err(CommandArgumentError::IndexOutOfBounds { idx: i });
911                    };
912                    (input_value.object_metadata.as_ref(), &mut input_value.inner)
913                }
914                Argument::Result(i) => {
915                    let Some(command_result) = self.results.get_mut(i as usize) else {
916                        return Err(CommandArgumentError::IndexOutOfBounds { idx: i });
917                    };
918                    if command_result.len() != 1 {
919                        return Err(CommandArgumentError::InvalidResultArity { result_idx: i });
920                    }
921                    (None, &mut command_result[0])
922                }
923                Argument::NestedResult(i, j) => {
924                    let Some(command_result) = self.results.get_mut(i as usize) else {
925                        return Err(CommandArgumentError::IndexOutOfBounds { idx: i });
926                    };
927                    let Some(result_value) = command_result.get_mut(j as usize) else {
928                        return Err(CommandArgumentError::SecondaryIndexOutOfBounds {
929                            result_idx: i,
930                            secondary_idx: j,
931                        });
932                    };
933                    (None, result_value)
934                }
935            };
936            if let Some(usage) = update_last_usage {
937                result_value.last_usage_kind = Some(usage);
938            }
939            Ok((metadata, &mut result_value.value))
940        }
941
942        /// Executes a Move function bypassing visibility checks, allowing the
943        /// execution of private or protected functions. This method
944        /// sets up the necessary gas status and data store, and then
945        /// delegates the execution to the Move VM runtime.
946        pub(crate) fn execute_function_bypass_visibility(
947            &mut self,
948            module: &ModuleId,
949            function_name: &IdentStr,
950            ty_args: Vec<Type>,
951            args: Vec<impl Borrow<[u8]>>,
952            tracer: &mut Option<MoveTraceBuilder>,
953        ) -> VMResult<SerializedReturnValues> {
954            let gas_status = self.gas_charger.move_gas_status_mut();
955            let mut data_store = IotaDataStore::new(&self.linkage_view, &self.new_packages);
956            self.vm.get_runtime().execute_function_bypass_visibility(
957                module,
958                function_name,
959                ty_args,
960                args,
961                &mut data_store,
962                gas_status,
963                &mut self.native_extensions,
964                tracer.as_mut(),
965            )
966        }
967
968        /// Loads a Move function from the specified module with the given type
969        /// arguments without executing it. This function initializes
970        /// the data store and delegates the loading process to the Move
971        /// VM runtime.
972        pub(crate) fn load_function(
973            &mut self,
974            module_id: &ModuleId,
975            function_name: &IdentStr,
976            type_arguments: &[Type],
977        ) -> VMResult<LoadedFunctionInstantiation> {
978            let mut data_store = IotaDataStore::new(&self.linkage_view, &self.new_packages);
979            self.vm.get_runtime().load_function(
980                module_id,
981                function_name,
982                type_arguments,
983                &mut data_store,
984            )
985        }
986
987        /// Constructs an `ObjectValue` based on the provided Move object type,
988        /// transferability, usage context, and byte contents. This
989        /// function utilizes the protocol configuration, Move VM, and
990        /// linkage view to properly interpret and instantiate the object.
991        pub(crate) fn make_object_value(
992            &mut self,
993            type_: MoveObjectType,
994            used_in_non_entry_move_call: bool,
995            contents: &[u8],
996        ) -> Result<ObjectValue, ExecutionError> {
997            make_object_value(
998                self.vm,
999                &mut self.linkage_view,
1000                &self.new_packages,
1001                type_,
1002                used_in_non_entry_move_call,
1003                contents,
1004            )
1005        }
1006
1007        /// Publishes a bundle of Move modules to the blockchain under the
1008        /// specified sender's account address. The function initializes
1009        /// a data store and delegates the publishing operation to the Move VM
1010        /// runtime.
1011        pub fn publish_module_bundle(
1012            &mut self,
1013            modules: Vec<Vec<u8>>,
1014            sender: AccountAddress,
1015        ) -> VMResult<()> {
1016            // TODO: publish_module_bundle() currently doesn't charge gas.
1017            // Do we want to charge there?
1018            let mut data_store = IotaDataStore::new(&self.linkage_view, &self.new_packages);
1019            self.vm.get_runtime().publish_module_bundle(
1020                modules,
1021                sender,
1022                &mut data_store,
1023                self.gas_charger.move_gas_status_mut(),
1024            )
1025        }
1026    }
1027
1028    impl TypeTagResolver for ExecutionContext<'_, '_, '_> {
1029        /// Retrieves the `TypeTag` corresponding to the provided `Type` by
1030        /// querying the Move VM runtime.
1031        fn get_type_tag(&self, type_: &Type) -> Result<TypeTag, ExecutionError> {
1032            self.vm
1033                .get_runtime()
1034                .get_type_tag(type_)
1035                .map_err(|e| self.convert_vm_error(e))
1036        }
1037    }
1038
1039    /// Fetch the package at `package_id` with a view to using it as a link
1040    /// context.  Produces an error if the object at that ID does not exist,
1041    /// or is not a package.
1042    fn package_for_linkage(
1043        linkage_view: &LinkageView,
1044        package_id: ObjectID,
1045    ) -> VMResult<PackageObject> {
1046        use move_binary_format::errors::PartialVMError;
1047        use move_core_types::vm_status::StatusCode;
1048
1049        match linkage_view.get_package_object(&package_id) {
1050            Ok(Some(package)) => Ok(package),
1051            Ok(None) => Err(PartialVMError::new(StatusCode::LINKER_ERROR)
1052                .with_message(format!("Cannot find link context {package_id} in store"))
1053                .finish(Location::Undefined)),
1054            Err(err) => Err(PartialVMError::new(StatusCode::LINKER_ERROR)
1055                .with_message(format!(
1056                    "Error loading link context {package_id} from store: {err}"
1057                ))
1058                .finish(Location::Undefined)),
1059        }
1060    }
1061
1062    /// Loads a `Type` from the given `StructTag`, retrieving the corresponding
1063    /// struct from the package in storage. The function sets up the linkage
1064    /// context to resolve the struct's module and verifies
1065    /// any type parameter constraints. If the struct has type parameters, they
1066    /// are recursively loaded and verified.
1067    pub fn load_type_from_struct(
1068        vm: &MoveVM,
1069        linkage_view: &mut LinkageView,
1070        new_packages: &[MovePackage],
1071        struct_tag: &StructTag,
1072    ) -> VMResult<Type> {
1073        fn verification_error<T>(code: StatusCode) -> VMResult<T> {
1074            Err(PartialVMError::new(code).finish(Location::Undefined))
1075        }
1076
1077        let StructTag {
1078            address,
1079            module,
1080            name,
1081            type_params,
1082        } = struct_tag;
1083
1084        // Load the package that the struct is defined in, in storage
1085        let defining_id = ObjectID::from_address(*address);
1086        let package = package_for_linkage(linkage_view, defining_id)?;
1087
1088        // Set the defining package as the link context while loading the
1089        // struct
1090        let original_address = linkage_view
1091            .set_linkage(package.move_package())
1092            .map_err(|e| {
1093                PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR)
1094                    .with_message(e.to_string())
1095                    .finish(Location::Undefined)
1096            })?;
1097
1098        let runtime_id = ModuleId::new(original_address, module.clone());
1099        let data_store = IotaDataStore::new(linkage_view, new_packages);
1100        let res = vm.get_runtime().load_type(&runtime_id, name, &data_store);
1101        linkage_view.reset_linkage();
1102        let (idx, struct_type) = res?;
1103
1104        // Recursively load type parameters, if necessary
1105        let type_param_constraints = struct_type.type_param_constraints();
1106        if type_param_constraints.len() != type_params.len() {
1107            return verification_error(StatusCode::NUMBER_OF_TYPE_ARGUMENTS_MISMATCH);
1108        }
1109
1110        if type_params.is_empty() {
1111            Ok(Type::Datatype(idx))
1112        } else {
1113            let loaded_type_params = type_params
1114                .iter()
1115                .map(|type_param| load_type(vm, linkage_view, new_packages, type_param))
1116                .collect::<VMResult<Vec<_>>>()?;
1117
1118            // Verify that the type parameter constraints on the struct are met
1119            for (constraint, param) in type_param_constraints.zip(&loaded_type_params) {
1120                let abilities = vm.get_runtime().get_type_abilities(param)?;
1121                if !constraint.is_subset(abilities) {
1122                    return verification_error(StatusCode::CONSTRAINT_NOT_SATISFIED);
1123                }
1124            }
1125
1126            Ok(Type::DatatypeInstantiation(Box::new((
1127                idx,
1128                loaded_type_params,
1129            ))))
1130        }
1131    }
1132
1133    /// Load `type_tag` to get a `Type` in the provided `session`.  `session`'s
1134    /// linkage context may be reset after this operation, because during
1135    /// the operation, it may change when loading a struct.
1136    pub fn load_type(
1137        vm: &MoveVM,
1138        linkage_view: &mut LinkageView,
1139        new_packages: &[MovePackage],
1140        type_tag: &TypeTag,
1141    ) -> VMResult<Type> {
1142        Ok(match type_tag {
1143            TypeTag::Bool => Type::Bool,
1144            TypeTag::U8 => Type::U8,
1145            TypeTag::U16 => Type::U16,
1146            TypeTag::U32 => Type::U32,
1147            TypeTag::U64 => Type::U64,
1148            TypeTag::U128 => Type::U128,
1149            TypeTag::U256 => Type::U256,
1150            TypeTag::Address => Type::Address,
1151            TypeTag::Signer => Type::Signer,
1152
1153            TypeTag::Vector(inner) => {
1154                Type::Vector(Box::new(load_type(vm, linkage_view, new_packages, inner)?))
1155            }
1156            TypeTag::Struct(struct_tag) => {
1157                return load_type_from_struct(vm, linkage_view, new_packages, struct_tag);
1158            }
1159        })
1160    }
1161
1162    /// Constructs an `ObjectValue` based on the provided `MoveObjectType`,
1163    /// contents, and additional flags such as transferability and usage
1164    /// context. If the object is a coin, it deserializes the contents into
1165    /// a `Coin` type; otherwise, it treats the contents as raw data. The
1166    /// function then loads the corresponding struct type from the Move
1167    /// package and verifies its abilities if needed.
1168    pub(crate) fn make_object_value(
1169        vm: &MoveVM,
1170        linkage_view: &mut LinkageView,
1171        new_packages: &[MovePackage],
1172        type_: MoveObjectType,
1173        used_in_non_entry_move_call: bool,
1174        contents: &[u8],
1175    ) -> Result<ObjectValue, ExecutionError> {
1176        let contents = if type_.is_coin() {
1177            let Ok(coin) = Coin::from_bcs_bytes(contents) else {
1178                invariant_violation!("Could not deserialize a coin")
1179            };
1180            ObjectContents::Coin(coin)
1181        } else {
1182            ObjectContents::Raw(contents.to_vec())
1183        };
1184
1185        let tag: StructTag = type_.into();
1186        let type_ = load_type_from_struct(vm, linkage_view, new_packages, &tag)
1187            .map_err(|e| crate::error::convert_vm_error(e, vm, linkage_view))?;
1188        let abilities = vm
1189            .get_runtime()
1190            .get_type_abilities(&type_)
1191            .map_err(|e| crate::error::convert_vm_error(e, vm, linkage_view))?;
1192        let has_public_transfer = abilities.has_store();
1193        Ok(ObjectValue {
1194            type_,
1195            has_public_transfer,
1196            used_in_non_entry_move_call,
1197            contents,
1198        })
1199    }
1200
1201    /// Converts a provided `Object` into an `ObjectValue`, extracting and
1202    /// validating the `MoveObjectType` and contents. This function assumes
1203    /// the object contains Move-specific data and passes the extracted data
1204    /// through `make_object_value` to create the corresponding `ObjectValue`.
1205    pub(crate) fn value_from_object(
1206        vm: &MoveVM,
1207        linkage_view: &mut LinkageView,
1208        new_packages: &[MovePackage],
1209        object: &Object,
1210    ) -> Result<ObjectValue, ExecutionError> {
1211        let ObjectInner {
1212            data: Data::Move(object),
1213            ..
1214        } = object.as_inner()
1215        else {
1216            invariant_violation!("Expected a Move object");
1217        };
1218
1219        let used_in_non_entry_move_call = false;
1220        make_object_value(
1221            vm,
1222            linkage_view,
1223            new_packages,
1224            object.type_().clone(),
1225            used_in_non_entry_move_call,
1226            object.contents(),
1227        )
1228    }
1229
1230    /// Load an input object from the state_view
1231    fn load_object(
1232        vm: &MoveVM,
1233        state_view: &dyn ExecutionState,
1234        linkage_view: &mut LinkageView,
1235        new_packages: &[MovePackage],
1236        input_object_map: &mut BTreeMap<ObjectID, object_runtime::InputObject>,
1237        override_as_immutable: bool,
1238        id: ObjectID,
1239    ) -> Result<InputValue, ExecutionError> {
1240        let Some(obj) = state_view.read_object(&id) else {
1241            // protected by transaction input checker
1242            invariant_violation!("Object {} does not exist yet", id);
1243        };
1244        // override_as_immutable ==> Owner::Shared
1245        assert_invariant!(
1246            !override_as_immutable || matches!(obj.owner, Owner::Shared { .. }),
1247            "override_as_immutable should only be set for shared objects"
1248        );
1249        let is_mutable_input = match obj.owner {
1250            Owner::AddressOwner(_) => true,
1251            Owner::Shared { .. } => !override_as_immutable,
1252            Owner::Immutable => false,
1253            Owner::ObjectOwner(_) => {
1254                // protected by transaction input checker
1255                invariant_violation!("ObjectOwner objects cannot be input")
1256            }
1257        };
1258        let owner = obj.owner;
1259        let version = obj.version();
1260        let object_metadata = InputObjectMetadata::InputObject {
1261            id,
1262            is_mutable_input,
1263            owner,
1264            version,
1265        };
1266        let obj_value = value_from_object(vm, linkage_view, new_packages, obj)?;
1267        let contained_uids = {
1268            let fully_annotated_layout = vm
1269                .get_runtime()
1270                .type_to_fully_annotated_layout(&obj_value.type_)
1271                .map_err(|e| convert_vm_error(e, vm, linkage_view))?;
1272            let mut bytes = vec![];
1273            obj_value.write_bcs_bytes(&mut bytes, None)?;
1274            match get_all_uids(&fully_annotated_layout, &bytes) {
1275                Err(e) => {
1276                    invariant_violation!("Unable to retrieve UIDs for object. Got error: {e}")
1277                }
1278                Ok(uids) => uids,
1279            }
1280        };
1281        let runtime_input = object_runtime::InputObject {
1282            contained_uids,
1283            owner,
1284            version,
1285        };
1286        let prev = input_object_map.insert(id, runtime_input);
1287        // protected by transaction input checker
1288        assert_invariant!(prev.is_none(), "Duplicate input object {}", id);
1289        Ok(InputValue::new_object(object_metadata, obj_value))
1290    }
1291
1292    /// Load an a CallArg, either an object or a raw set of BCS bytes
1293    fn load_call_arg(
1294        vm: &MoveVM,
1295        state_view: &dyn ExecutionState,
1296        linkage_view: &mut LinkageView,
1297        new_packages: &[MovePackage],
1298        input_object_map: &mut BTreeMap<ObjectID, object_runtime::InputObject>,
1299        call_arg: CallArg,
1300    ) -> Result<InputValue, ExecutionError> {
1301        Ok(match call_arg {
1302            CallArg::Pure(bytes) => InputValue::new_raw(RawValueType::Any, bytes),
1303            CallArg::Object(obj_arg) => load_object_arg(
1304                vm,
1305                state_view,
1306                linkage_view,
1307                new_packages,
1308                input_object_map,
1309                obj_arg,
1310            )?,
1311        })
1312    }
1313
1314    /// Load an ObjectArg from state view, marking if it can be treated as
1315    /// mutable or not
1316    fn load_object_arg(
1317        vm: &MoveVM,
1318        state_view: &dyn ExecutionState,
1319        linkage_view: &mut LinkageView,
1320        new_packages: &[MovePackage],
1321        input_object_map: &mut BTreeMap<ObjectID, object_runtime::InputObject>,
1322        obj_arg: ObjectArg,
1323    ) -> Result<InputValue, ExecutionError> {
1324        match obj_arg {
1325            ObjectArg::ImmOrOwnedObject((id, _, _)) => load_object(
1326                vm,
1327                state_view,
1328                linkage_view,
1329                new_packages,
1330                input_object_map,
1331                // imm override
1332                false,
1333                id,
1334            ),
1335            ObjectArg::SharedObject { id, mutable, .. } => load_object(
1336                vm,
1337                state_view,
1338                linkage_view,
1339                new_packages,
1340                input_object_map,
1341                // imm override
1342                !mutable,
1343                id,
1344            ),
1345            ObjectArg::Receiving((id, version, _)) => {
1346                Ok(InputValue::new_receiving_object(id, version))
1347            }
1348        }
1349    }
1350
1351    /// Generate an additional write for an ObjectValue
1352    fn add_additional_write(
1353        additional_writes: &mut BTreeMap<ObjectID, AdditionalWrite>,
1354        owner: Owner,
1355        object_value: ObjectValue,
1356    ) -> Result<(), ExecutionError> {
1357        let ObjectValue {
1358            type_, contents, ..
1359        } = object_value;
1360        let bytes = match contents {
1361            ObjectContents::Coin(coin) => coin.to_bcs_bytes(),
1362            ObjectContents::Raw(bytes) => bytes,
1363        };
1364        let object_id = MoveObject::id_opt(&bytes).map_err(|e| {
1365            ExecutionError::invariant_violation(format!("No id for Raw object bytes. {e}"))
1366        })?;
1367        let additional_write = AdditionalWrite {
1368            recipient: owner,
1369            type_,
1370            bytes,
1371        };
1372        additional_writes.insert(object_id, additional_write);
1373        Ok(())
1374    }
1375
1376    /// The max budget was deducted from the gas coin at the beginning of the
1377    /// transaction, now we return exactly that amount. Gas will be charged
1378    /// by the execution engine
1379    fn refund_max_gas_budget(
1380        additional_writes: &mut BTreeMap<ObjectID, AdditionalWrite>,
1381        gas_charger: &mut GasCharger,
1382        gas_id: ObjectID,
1383    ) -> Result<(), ExecutionError> {
1384        let Some(AdditionalWrite { bytes, .. }) = additional_writes.get_mut(&gas_id) else {
1385            invariant_violation!("Gas object cannot be wrapped or destroyed")
1386        };
1387        let Ok(mut coin) = Coin::from_bcs_bytes(bytes) else {
1388            invariant_violation!("Gas object must be a coin")
1389        };
1390        let Some(new_balance) = coin.balance.value().checked_add(gas_charger.gas_budget()) else {
1391            return Err(ExecutionError::new_with_source(
1392                ExecutionErrorKind::CoinBalanceOverflow,
1393                "Gas coin too large after returning the max gas budget",
1394            ));
1395        };
1396        coin.balance = Balance::new(new_balance);
1397        *bytes = coin.to_bcs_bytes();
1398        Ok(())
1399    }
1400
1401    /// Generate an MoveObject given an updated/written object
1402    fn create_written_object(
1403        vm: &MoveVM,
1404        linkage_view: &LinkageView,
1405        protocol_config: &ProtocolConfig,
1406        objects_modified_at: &BTreeMap<ObjectID, LoadedRuntimeObject>,
1407        id: ObjectID,
1408        type_: Type,
1409        contents: Vec<u8>,
1410    ) -> Result<MoveObject, ExecutionError> {
1411        debug_assert_eq!(
1412            id,
1413            MoveObject::id_opt(&contents).expect("object contents should start with an id")
1414        );
1415        let old_obj_ver = objects_modified_at
1416            .get(&id)
1417            .map(|obj: &LoadedRuntimeObject| obj.version);
1418
1419        let type_tag = vm
1420            .get_runtime()
1421            .get_type_tag(&type_)
1422            .map_err(|e| crate::error::convert_vm_error(e, vm, linkage_view))?;
1423
1424        let struct_tag = match type_tag {
1425            TypeTag::Struct(inner) => *inner,
1426            _ => invariant_violation!("Non struct type for object"),
1427        };
1428        MoveObject::new_from_execution(
1429            struct_tag.into(),
1430            old_obj_ver.unwrap_or_default(),
1431            contents,
1432            protocol_config,
1433        )
1434    }
1435
1436    // Implementation of the `DataStore` trait for the Move VM.
1437    // When used during execution it may have a list of new packages that have
1438    // just been published in the current context. Those are used for module/type
1439    // resolution when executing module init.
1440    // It may be created with an empty slice of packages either when no
1441    // publish/upgrade are performed or when a type is requested not during
1442    // execution.
1443    pub(crate) struct IotaDataStore<'state, 'a> {
1444        linkage_view: &'a LinkageView<'state>,
1445        new_packages: &'a [MovePackage],
1446    }
1447
1448    impl<'state, 'a> IotaDataStore<'state, 'a> {
1449        pub(crate) fn new(
1450            linkage_view: &'a LinkageView<'state>,
1451            new_packages: &'a [MovePackage],
1452        ) -> Self {
1453            Self {
1454                linkage_view,
1455                new_packages,
1456            }
1457        }
1458
1459        fn get_module(&self, module_id: &ModuleId) -> Option<&Vec<u8>> {
1460            for package in self.new_packages {
1461                let module = package.get_module(module_id);
1462                if module.is_some() {
1463                    return module;
1464                }
1465            }
1466            None
1467        }
1468    }
1469
1470    // TODO: `DataStore` will be reworked and this is likely to disappear.
1471    //       Leaving this comment around until then as testament to better days to
1472    // come...
1473    impl DataStore for IotaDataStore<'_, '_> {
1474        fn link_context(&self) -> AccountAddress {
1475            self.linkage_view.link_context()
1476        }
1477
1478        fn relocate(&self, module_id: &ModuleId) -> PartialVMResult<ModuleId> {
1479            self.linkage_view.relocate(module_id).map_err(|err| {
1480                PartialVMError::new(StatusCode::LINKER_ERROR)
1481                    .with_message(format!("Error relocating {module_id}: {err:?}"))
1482            })
1483        }
1484
1485        fn defining_module(
1486            &self,
1487            runtime_id: &ModuleId,
1488            struct_: &IdentStr,
1489        ) -> PartialVMResult<ModuleId> {
1490            self.linkage_view
1491                .defining_module(runtime_id, struct_)
1492                .map_err(|err| {
1493                    PartialVMError::new(StatusCode::LINKER_ERROR).with_message(format!(
1494                        "Error finding defining module for {runtime_id}::{struct_}: {err:?}"
1495                    ))
1496                })
1497        }
1498
1499        fn load_module(&self, module_id: &ModuleId) -> VMResult<Vec<u8>> {
1500            if let Some(bytes) = self.get_module(module_id) {
1501                return Ok(bytes.clone());
1502            }
1503            match self.linkage_view.get_module(module_id) {
1504                Ok(Some(bytes)) => Ok(bytes),
1505                Ok(None) => Err(PartialVMError::new(StatusCode::LINKER_ERROR)
1506                    .with_message(format!("Cannot find {:?} in data cache", module_id))
1507                    .finish(Location::Undefined)),
1508                Err(err) => {
1509                    let msg = format!("Unexpected storage error: {:?}", err);
1510                    Err(
1511                        PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR)
1512                            .with_message(msg)
1513                            .finish(Location::Undefined),
1514                    )
1515                }
1516            }
1517        }
1518
1519        fn publish_module(&mut self, _module_id: &ModuleId, _blob: Vec<u8>) -> VMResult<()> {
1520            // we cannot panic here because during execution and publishing this is
1521            // currently called from the publish flow in the Move runtime
1522            Ok(())
1523        }
1524    }
1525}