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