sui_adapter_v2/programmable_transactions/
context.rs

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