sui_adapter_latest/static_programmable_transactions/execution/
context.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use crate::{
5    adapter,
6    data_store::linked_data_store::LinkedDataStore,
7    execution_mode::ExecutionMode,
8    gas_charger::GasCharger,
9    gas_meter::SuiGasMeter,
10    programmable_transactions::{self as legacy_ptb},
11    sp,
12    static_programmable_transactions::{
13        env::Env,
14        execution::{
15            trace_utils,
16            values::{Local, Locals, Value},
17        },
18        linkage::resolved_linkage::{ResolvedLinkage, RootedLinkage},
19        loading::ast::{Datatype, ObjectMutability},
20        typing::ast::{self as T, Type},
21    },
22};
23use indexmap::{IndexMap, IndexSet};
24use move_binary_format::{
25    CompiledModule,
26    errors::{Location, PartialVMError, VMResult},
27    file_format::FunctionDefinitionIndex,
28    file_format_common::VERSION_6,
29};
30use move_core_types::{
31    account_address::AccountAddress,
32    identifier::IdentStr,
33    language_storage::{ModuleId, StructTag},
34};
35use move_trace_format::format::MoveTraceBuilder;
36use move_vm_runtime::native_extensions::NativeContextExtensions;
37use move_vm_types::{
38    gas::{GasMeter, SimpleInstruction},
39    values::{VMValueCast, Value as VMValue},
40};
41use std::{
42    cell::RefCell,
43    collections::{BTreeMap, BTreeSet},
44    rc::Rc,
45    sync::Arc,
46};
47use sui_move_natives::object_runtime::{
48    self, LoadedRuntimeObject, ObjectRuntime, RuntimeResults, get_all_uids, max_event_error,
49};
50use sui_types::{
51    TypeTag,
52    base_types::{MoveObjectType, ObjectID, SequenceNumber, TxContext},
53    error::{ExecutionError, ExecutionErrorKind},
54    execution::ExecutionResults,
55    metrics::LimitsMetrics,
56    move_package::{MovePackage, UpgradeCap, UpgradeReceipt, UpgradeTicket},
57    object::{MoveObject, Object, Owner},
58};
59use sui_verifier::INIT_FN_NAME;
60use tracing::instrument;
61
62macro_rules! unwrap {
63    ($e:expr, $($args:expr),* $(,)?) => {
64        match $e {
65            Some(v) => v,
66            None => {
67                invariant_violation!("Unexpected none: {}", format!($($args),*))
68            }
69        }
70
71    };
72}
73
74macro_rules! object_runtime_mut {
75    ($context:ident) => {{
76        $context
77            .native_extensions
78            .get_mut::<ObjectRuntime>()
79            .map_err(|e| $context.env.convert_vm_error(e.finish(Location::Undefined)))
80    }};
81}
82
83macro_rules! charge_gas_ {
84    ($gas_charger:expr, $env:expr, $call:ident($($args:expr),*)) => {{
85        SuiGasMeter($gas_charger.move_gas_status_mut())
86            .$call($($args),*)
87            .map_err(|e| $env.convert_vm_error(e.finish(Location::Undefined)))
88    }};
89    ($gas_charger:expr, $env:expr, $case:ident, $value_view:expr) => {
90        charge_gas_!($gas_charger, $env, $case($value_view))
91    };
92}
93
94macro_rules! charge_gas {
95    ($context:ident, $case:ident, $value_view:expr) => {{ charge_gas_!($context.gas_charger, $context.env, $case, $value_view) }};
96}
97
98/// Type wrapper around Value to ensure safe usage
99#[derive(Debug)]
100pub struct CtxValue(Value);
101
102#[derive(Clone, Debug)]
103pub struct InputObjectMetadata {
104    pub id: ObjectID,
105    pub mutability: ObjectMutability,
106    pub owner: Owner,
107    pub version: SequenceNumber,
108    pub type_: Type,
109}
110
111#[derive(Copy, Clone)]
112enum UsageKind {
113    Move,
114    Copy,
115    Borrow,
116}
117
118// Locals and metadata for all `Location`s. Separated from `Context` for lifetime reasons.
119struct Locations {
120    // A single local for holding the TxContext
121    tx_context_value: Locals,
122    /// The runtime value for the Gas coin, None if no gas coin is provided
123    gas: Option<(InputObjectMetadata, Locals)>,
124    /// The runtime value for the input objects args
125    input_object_metadata: Vec<(T::InputIndex, InputObjectMetadata)>,
126    object_inputs: Locals,
127    input_withdrawal_metadata: Vec<T::WithdrawalInput>,
128    withdrawal_inputs: Locals,
129    pure_input_bytes: IndexSet<Vec<u8>>,
130    pure_input_metadata: Vec<T::PureInput>,
131    pure_inputs: Locals,
132    receiving_input_metadata: Vec<T::ReceivingInput>,
133    receiving_inputs: Locals,
134    /// The results of a given command. For most commands, the inner vector will have length 1.
135    /// It will only not be 1 for Move calls with multiple return values.
136    /// Inner values are None if taken/moved by-value
137    results: Vec<Locals>,
138}
139
140enum ResolvedLocation<'a> {
141    Local(Local<'a>),
142    Pure {
143        bytes: &'a [u8],
144        metadata: &'a T::PureInput,
145        local: Local<'a>,
146    },
147    Receiving {
148        metadata: &'a T::ReceivingInput,
149        local: Local<'a>,
150    },
151}
152
153/// Maintains all runtime state specific to programmable transactions
154pub struct Context<'env, 'pc, 'vm, 'state, 'linkage, 'gas> {
155    pub env: &'env Env<'pc, 'vm, 'state, 'linkage>,
156    /// Metrics for reporting exceeded limits
157    pub metrics: Arc<LimitsMetrics>,
158    pub native_extensions: NativeContextExtensions<'env>,
159    /// A shared transaction context, contains transaction digest information and manages the
160    /// creation of new object IDs
161    pub tx_context: Rc<RefCell<TxContext>>,
162    /// The gas charger used for metering
163    pub gas_charger: &'gas mut GasCharger,
164    /// User events are claimed after each Move call
165    user_events: Vec<(ModuleId, StructTag, Vec<u8>)>,
166    // runtime data
167    locations: Locations,
168}
169
170impl Locations {
171    /// NOTE! This does not charge gas and should not be used directly. It is exposed for
172    /// dev-inspect
173    fn resolve(&mut self, location: T::Location) -> Result<ResolvedLocation<'_>, ExecutionError> {
174        Ok(match location {
175            T::Location::TxContext => ResolvedLocation::Local(self.tx_context_value.local(0)?),
176            T::Location::GasCoin => {
177                let (_, gas_locals) = unwrap!(self.gas.as_mut(), "Gas coin not provided");
178                ResolvedLocation::Local(gas_locals.local(0)?)
179            }
180            T::Location::ObjectInput(i) => ResolvedLocation::Local(self.object_inputs.local(i)?),
181            T::Location::WithdrawalInput(i) => {
182                ResolvedLocation::Local(self.withdrawal_inputs.local(i)?)
183            }
184            T::Location::Result(i, j) => {
185                let result = unwrap!(self.results.get_mut(i as usize), "bounds already verified");
186                ResolvedLocation::Local(result.local(j)?)
187            }
188            T::Location::PureInput(i) => {
189                let local = self.pure_inputs.local(i)?;
190                let metadata = &self.pure_input_metadata[i as usize];
191                let bytes = self
192                    .pure_input_bytes
193                    .get_index(metadata.byte_index)
194                    .ok_or_else(|| {
195                        make_invariant_violation!(
196                            "Pure input {} bytes out of bounds at index {}",
197                            metadata.original_input_index.0,
198                            metadata.byte_index,
199                        )
200                    })?;
201                ResolvedLocation::Pure {
202                    bytes,
203                    metadata,
204                    local,
205                }
206            }
207            T::Location::ReceivingInput(i) => ResolvedLocation::Receiving {
208                metadata: &self.receiving_input_metadata[i as usize],
209                local: self.receiving_inputs.local(i)?,
210            },
211        })
212    }
213}
214
215impl<'env, 'pc, 'vm, 'state, 'linkage, 'gas> Context<'env, 'pc, 'vm, 'state, 'linkage, 'gas> {
216    #[instrument(name = "Context::new", level = "trace", skip_all)]
217    pub fn new(
218        env: &'env Env<'pc, 'vm, 'state, 'linkage>,
219        metrics: Arc<LimitsMetrics>,
220        tx_context: Rc<RefCell<TxContext>>,
221        gas_charger: &'gas mut GasCharger,
222        pure_input_bytes: IndexSet<Vec<u8>>,
223        object_inputs: Vec<T::ObjectInput>,
224        input_withdrawal_metadata: Vec<T::WithdrawalInput>,
225        pure_input_metadata: Vec<T::PureInput>,
226        receiving_input_metadata: Vec<T::ReceivingInput>,
227    ) -> Result<Self, ExecutionError>
228    where
229        'pc: 'state,
230    {
231        let mut input_object_map = BTreeMap::new();
232        let mut input_object_metadata = Vec::with_capacity(object_inputs.len());
233        let mut object_values = Vec::with_capacity(object_inputs.len());
234        for object_input in object_inputs {
235            let (i, m, v) = load_object_arg(gas_charger, env, &mut input_object_map, object_input)?;
236            input_object_metadata.push((i, m));
237            object_values.push(Some(v));
238        }
239        let object_inputs = Locals::new(object_values)?;
240        let mut withdrawal_values = Vec::with_capacity(input_withdrawal_metadata.len());
241        for withdrawal_input in &input_withdrawal_metadata {
242            let v = load_withdrawal_arg(gas_charger, env, withdrawal_input)?;
243            withdrawal_values.push(Some(v));
244        }
245        let withdrawal_inputs = Locals::new(withdrawal_values)?;
246        let pure_inputs = Locals::new_invalid(pure_input_metadata.len())?;
247        let receiving_inputs = Locals::new_invalid(receiving_input_metadata.len())?;
248        let gas = match gas_charger.gas_coin() {
249            Some(gas_coin) => {
250                let ty = env.gas_coin_type()?;
251                let (gas_metadata, gas_value) = load_object_arg_impl(
252                    gas_charger,
253                    env,
254                    &mut input_object_map,
255                    gas_coin,
256                    ObjectMutability::Mutable,
257                    ty,
258                )?;
259                let mut gas_locals = Locals::new([Some(gas_value)])?;
260                let gas_local = gas_locals.local(0)?;
261                let gas_ref = gas_local.borrow()?;
262                // We have already checked that the gas balance is enough to cover the gas budget
263                let max_gas_in_balance = gas_charger.gas_budget();
264                gas_ref.coin_ref_subtract_balance(max_gas_in_balance)?;
265                Some((gas_metadata, gas_locals))
266            }
267            None => None,
268        };
269        let native_extensions = adapter::new_native_extensions(
270            env.state_view.as_child_resolver(),
271            input_object_map,
272            !gas_charger.is_unmetered(),
273            env.protocol_config,
274            metrics.clone(),
275            tx_context.clone(),
276        );
277
278        debug_assert_eq!(gas_charger.move_gas_status().stack_height_current(), 0);
279        let tx_context_value = Locals::new(vec![Some(Value::new_tx_context(
280            tx_context.borrow().digest(),
281        )?)])?;
282        Ok(Self {
283            env,
284            metrics,
285            native_extensions,
286            tx_context,
287            gas_charger,
288            user_events: vec![],
289            locations: Locations {
290                tx_context_value,
291                gas,
292                input_object_metadata,
293                object_inputs,
294                input_withdrawal_metadata,
295                withdrawal_inputs,
296                pure_input_bytes,
297                pure_input_metadata,
298                pure_inputs,
299                receiving_input_metadata,
300                receiving_inputs,
301                results: vec![],
302            },
303        })
304    }
305
306    pub fn finish<Mode: ExecutionMode>(mut self) -> Result<ExecutionResults, ExecutionError> {
307        assert_invariant!(
308            !self.locations.tx_context_value.local(0)?.is_invalid()?,
309            "tx context value should be present"
310        );
311        let gas = std::mem::take(&mut self.locations.gas);
312        let object_input_metadata = std::mem::take(&mut self.locations.input_object_metadata);
313        let mut object_inputs =
314            std::mem::replace(&mut self.locations.object_inputs, Locals::new_invalid(0)?);
315        let mut loaded_runtime_objects = BTreeMap::new();
316        let mut by_value_shared_objects = BTreeSet::new();
317        let mut consensus_owner_objects = BTreeMap::new();
318        let gas = gas
319            .map(|(m, mut g)| Result::<_, ExecutionError>::Ok((m, g.local(0)?.move_if_valid()?)))
320            .transpose()?;
321        let gas_id_opt = gas.as_ref().map(|(m, _)| m.id);
322        let object_inputs = object_input_metadata
323            .into_iter()
324            .enumerate()
325            .map(|(i, (_, m))| {
326                let v_opt = object_inputs.local(i as u16)?.move_if_valid()?;
327                Ok((m, v_opt))
328            })
329            .collect::<Result<Vec<_>, ExecutionError>>()?;
330        for (metadata, value_opt) in object_inputs.into_iter().chain(gas) {
331            let InputObjectMetadata {
332                id,
333                mutability,
334                owner,
335                version,
336                type_,
337            } = metadata;
338            match mutability {
339                ObjectMutability::Immutable => continue,
340                // It is illegal to mutate NonExclusiveWrites, but they are passed as &mut T,
341                // so we need to treat them as mutable here. After execution, we check if they
342                // have been mutated, and abort the tx if they have.
343                ObjectMutability::NonExclusiveWrite | ObjectMutability::Mutable => (),
344            }
345            loaded_runtime_objects.insert(
346                id,
347                LoadedRuntimeObject {
348                    version,
349                    is_modified: true,
350                },
351            );
352            if let Some(object) = value_opt {
353                self.transfer_object_(
354                    owner,
355                    type_,
356                    CtxValue(object),
357                    /* end of transaction */ true,
358                )?;
359            } else if owner.is_shared() {
360                by_value_shared_objects.insert(id);
361            } else if matches!(owner, Owner::ConsensusAddressOwner { .. }) {
362                consensus_owner_objects.insert(id, owner.clone());
363            }
364        }
365
366        let Self {
367            env,
368            mut native_extensions,
369            tx_context,
370            gas_charger,
371            user_events,
372            ..
373        } = self;
374        let ref_context: &RefCell<TxContext> = &tx_context;
375        let tx_context: &TxContext = &ref_context.borrow();
376        let tx_digest = ref_context.borrow().digest();
377
378        let object_runtime: ObjectRuntime = native_extensions
379            .remove()
380            .map_err(|e| env.convert_vm_error(e.finish(Location::Undefined)))?;
381
382        let RuntimeResults {
383            mut writes,
384            user_events: remaining_events,
385            loaded_child_objects,
386            mut created_object_ids,
387            deleted_object_ids,
388            accumulator_events,
389            settlement_input_sui,
390            settlement_output_sui,
391        } = object_runtime.finish()?;
392        assert_invariant!(
393            remaining_events.is_empty(),
394            "Events should be taken after every Move call"
395        );
396        // Refund unused gas
397        if let Some(gas_id) = gas_id_opt {
398            refund_max_gas_budget(&mut writes, gas_charger, gas_id)?;
399        }
400
401        loaded_runtime_objects.extend(loaded_child_objects);
402
403        let mut written_objects = BTreeMap::new();
404
405        for (id, (recipient, ty, value)) in writes {
406            let (ty, layout) = env.load_type_and_layout_from_struct(&ty.clone().into())?;
407            let abilities = ty.abilities();
408            let has_public_transfer = abilities.has_store();
409            let Some(bytes) = value.typed_serialize(&layout) else {
410                invariant_violation!("Failed to serialize already deserialized Move value");
411            };
412            // safe because has_public_transfer has been determined by the abilities
413            let move_object = unsafe {
414                create_written_object::<Mode>(
415                    env,
416                    &loaded_runtime_objects,
417                    id,
418                    ty,
419                    has_public_transfer,
420                    bytes,
421                )?
422            };
423            let object = Object::new_move(move_object, recipient, tx_digest);
424            written_objects.insert(id, object);
425        }
426
427        for package in self.env.linkable_store.to_new_packages().into_iter() {
428            let package_obj = Object::new_from_package(package, tx_digest);
429            let id = package_obj.id();
430            created_object_ids.insert(id);
431            written_objects.insert(id, package_obj);
432        }
433
434        legacy_ptb::context::finish(
435            env.protocol_config,
436            env.state_view,
437            gas_charger,
438            tx_context,
439            &by_value_shared_objects,
440            &consensus_owner_objects,
441            loaded_runtime_objects,
442            written_objects,
443            created_object_ids,
444            deleted_object_ids,
445            user_events,
446            accumulator_events,
447            settlement_input_sui,
448            settlement_output_sui,
449        )
450    }
451
452    pub fn object_runtime(&self) -> Result<&ObjectRuntime<'_>, ExecutionError> {
453        self.native_extensions
454            .get::<ObjectRuntime>()
455            .map_err(|e| self.env.convert_vm_error(e.finish(Location::Undefined)))
456    }
457
458    pub fn take_user_events(
459        &mut self,
460        storage_id: ModuleId,
461        function_def_idx: FunctionDefinitionIndex,
462        instr_length: u16,
463        linkage: &RootedLinkage,
464    ) -> Result<(), ExecutionError> {
465        let events = object_runtime_mut!(self)?.take_user_events();
466        let Some(num_events) = self.user_events.len().checked_add(events.len()) else {
467            invariant_violation!("usize overflow, too many events emitted")
468        };
469        let max_events = self.env.protocol_config.max_num_event_emit();
470        if num_events as u64 > max_events {
471            let err = max_event_error(max_events)
472                .at_code_offset(function_def_idx, instr_length)
473                .finish(Location::Module(storage_id.clone()));
474            return Err(self.env.convert_linked_vm_error(err, linkage));
475        }
476        let new_events = events
477            .into_iter()
478            .map(|(tag, value)| {
479                let layout = self.env.type_layout_for_struct(&tag)?;
480                let Some(bytes) = value.typed_serialize(&layout) else {
481                    invariant_violation!("Failed to serialize Move event");
482                };
483                Ok((storage_id.clone(), tag, bytes))
484            })
485            .collect::<Result<Vec<_>, ExecutionError>>()?;
486        self.user_events.extend(new_events);
487        Ok(())
488    }
489
490    //
491    // Arguments and Values
492    //
493
494    fn location(
495        &mut self,
496        usage: UsageKind,
497        location: T::Location,
498    ) -> Result<Value, ExecutionError> {
499        let resolved = self.locations.resolve(location)?;
500        let mut local = match resolved {
501            ResolvedLocation::Local(l) => l,
502            ResolvedLocation::Pure {
503                bytes,
504                metadata,
505                mut local,
506            } => {
507                if local.is_invalid()? {
508                    let v = load_pure_value(self.gas_charger, self.env, bytes, metadata)?;
509                    local.store(v)?;
510                }
511                local
512            }
513            ResolvedLocation::Receiving {
514                metadata,
515                mut local,
516            } => {
517                if local.is_invalid()? {
518                    let v = load_receiving_value(self.gas_charger, self.env, metadata)?;
519                    local.store(v)?;
520                }
521                local
522            }
523        };
524        Ok(match usage {
525            UsageKind::Move => {
526                let value = local.move_()?;
527                charge_gas_!(self.gas_charger, self.env, charge_move_loc, &value)?;
528                value
529            }
530            UsageKind::Copy => {
531                let value = local.copy()?;
532                charge_gas_!(self.gas_charger, self.env, charge_copy_loc, &value)?;
533                value
534            }
535            UsageKind::Borrow => {
536                charge_gas_!(
537                    self.gas_charger,
538                    self.env,
539                    charge_simple_instr(SimpleInstruction::MutBorrowLoc)
540                )?;
541                local.borrow()?
542            }
543        })
544    }
545
546    fn location_usage(&mut self, usage: T::Usage) -> Result<Value, ExecutionError> {
547        match usage {
548            T::Usage::Move(location) => self.location(UsageKind::Move, location),
549            T::Usage::Copy { location, .. } => self.location(UsageKind::Copy, location),
550        }
551    }
552
553    fn argument_value(&mut self, sp!(_, (arg_, _)): T::Argument) -> Result<Value, ExecutionError> {
554        match arg_ {
555            T::Argument__::Use(usage) => self.location_usage(usage),
556            // freeze is a no-op for references since the value does not track mutability
557            T::Argument__::Freeze(usage) => self.location_usage(usage),
558            T::Argument__::Borrow(_, location) => self.location(UsageKind::Borrow, location),
559            T::Argument__::Read(usage) => {
560                let reference = self.location_usage(usage)?;
561                charge_gas!(self, charge_read_ref, &reference)?;
562                reference.read_ref()
563            }
564        }
565    }
566
567    pub fn argument<V>(&mut self, arg: T::Argument) -> Result<V, ExecutionError>
568    where
569        VMValue: VMValueCast<V>,
570    {
571        let before_height = self.gas_charger.move_gas_status().stack_height_current();
572        let value = self.argument_value(arg)?;
573        let after_height = self.gas_charger.move_gas_status().stack_height_current();
574        debug_assert_eq!(before_height.saturating_add(1), after_height);
575        let value: V = value.cast()?;
576        Ok(value)
577    }
578
579    pub fn arguments<V>(&mut self, args: Vec<T::Argument>) -> Result<Vec<V>, ExecutionError>
580    where
581        VMValue: VMValueCast<V>,
582    {
583        args.into_iter().map(|arg| self.argument(arg)).collect()
584    }
585
586    pub fn result(&mut self, result: Vec<Option<CtxValue>>) -> Result<(), ExecutionError> {
587        self.locations
588            .results
589            .push(Locals::new(result.into_iter().map(|v| v.map(|v| v.0)))?);
590        Ok(())
591    }
592
593    pub fn charge_command(
594        &mut self,
595        is_move_call: bool,
596        num_args: usize,
597        num_return: usize,
598    ) -> Result<(), ExecutionError> {
599        let move_gas_status = self.gas_charger.move_gas_status_mut();
600        let before_size = move_gas_status.stack_size_current();
601        // Pop all of the arguments
602        // If the return values came from the Move VM directly (via a Move call), pop those
603        // as well
604        let num_popped = if is_move_call {
605            num_args.checked_add(num_return).ok_or_else(|| {
606                make_invariant_violation!("usize overflow when charging gas for command",)
607            })?
608        } else {
609            num_args
610        };
611        move_gas_status
612            .charge(1, 0, num_popped as u64, 0, /* unused */ 1)
613            .map_err(|e| self.env.convert_vm_error(e.finish(Location::Undefined)))?;
614        let after_size = move_gas_status.stack_size_current();
615        assert_invariant!(
616            before_size == after_size,
617            "We assume currently that the stack size is not decremented. \
618            If this changes, we need to actually account for it here"
619        );
620        Ok(())
621    }
622
623    pub fn copy_value(&mut self, value: &CtxValue) -> Result<CtxValue, ExecutionError> {
624        Ok(CtxValue(copy_value(self.gas_charger, self.env, &value.0)?))
625    }
626
627    pub fn new_coin(&mut self, amount: u64) -> Result<CtxValue, ExecutionError> {
628        let id = self.tx_context.borrow_mut().fresh_id();
629        object_runtime_mut!(self)?
630            .new_id(id)
631            .map_err(|e| self.env.convert_vm_error(e.finish(Location::Undefined)))?;
632        Ok(CtxValue(Value::coin(id, amount)))
633    }
634
635    pub fn destroy_coin(&mut self, coin: CtxValue) -> Result<u64, ExecutionError> {
636        let (id, amount) = coin.0.unpack_coin()?;
637        object_runtime_mut!(self)?
638            .delete_id(id)
639            .map_err(|e| self.env.convert_vm_error(e.finish(Location::Undefined)))?;
640        Ok(amount)
641    }
642
643    pub fn new_upgrade_cap(&mut self, storage_id: ObjectID) -> Result<CtxValue, ExecutionError> {
644        let id = self.tx_context.borrow_mut().fresh_id();
645        object_runtime_mut!(self)?
646            .new_id(id)
647            .map_err(|e| self.env.convert_vm_error(e.finish(Location::Undefined)))?;
648        let cap = UpgradeCap::new(id, storage_id);
649        Ok(CtxValue(Value::upgrade_cap(cap)))
650    }
651
652    pub fn upgrade_receipt(
653        &self,
654        upgrade_ticket: UpgradeTicket,
655        upgraded_package_id: ObjectID,
656    ) -> CtxValue {
657        let receipt = UpgradeReceipt::new(upgrade_ticket, upgraded_package_id);
658        CtxValue(Value::upgrade_receipt(receipt))
659    }
660
661    //
662    // Move calls
663    //
664
665    pub fn vm_move_call(
666        &mut self,
667        function: T::LoadedFunction,
668        args: Vec<CtxValue>,
669        trace_builder_opt: &mut Option<MoveTraceBuilder>,
670    ) -> Result<Vec<CtxValue>, ExecutionError> {
671        let result = self.execute_function_bypass_visibility(
672            &function.runtime_id,
673            &function.name,
674            &function.type_arguments,
675            args,
676            &function.linkage,
677            trace_builder_opt,
678        )?;
679        self.take_user_events(
680            function.storage_id,
681            function.definition_index,
682            function.instruction_length,
683            &function.linkage,
684        )?;
685        Ok(result)
686    }
687
688    pub fn execute_function_bypass_visibility(
689        &mut self,
690        runtime_id: &ModuleId,
691        function_name: &IdentStr,
692        ty_args: &[Type],
693        args: Vec<CtxValue>,
694        linkage: &RootedLinkage,
695        tracer: &mut Option<MoveTraceBuilder>,
696    ) -> Result<Vec<CtxValue>, ExecutionError> {
697        let ty_args = ty_args
698            .iter()
699            .enumerate()
700            .map(|(idx, ty)| self.env.load_vm_type_argument_from_adapter_type(idx, ty))
701            .collect::<Result<_, _>>()?;
702        let gas_status = self.gas_charger.move_gas_status_mut();
703        let mut data_store = LinkedDataStore::new(linkage, self.env.linkable_store);
704        let values = self
705            .env
706            .vm
707            .get_runtime()
708            .execute_function_with_values_bypass_visibility(
709                runtime_id,
710                function_name,
711                ty_args,
712                args.into_iter().map(|v| v.0.into()).collect(),
713                &mut data_store,
714                &mut SuiGasMeter(gas_status),
715                &mut self.native_extensions,
716                tracer.as_mut(),
717            )
718            .map_err(|e| self.env.convert_linked_vm_error(e, linkage))?;
719        Ok(values.into_iter().map(|v| CtxValue(v.into())).collect())
720    }
721
722    //
723    // Publish and Upgrade
724    //
725
726    // is_upgrade is used for gas charging. Assumed to be a new publish if false.
727    pub fn deserialize_modules(
728        &mut self,
729        module_bytes: &[Vec<u8>],
730        is_upgrade: bool,
731    ) -> Result<Vec<CompiledModule>, ExecutionError> {
732        assert_invariant!(
733            !module_bytes.is_empty(),
734            "empty package is checked in transaction input checker"
735        );
736        let total_bytes = module_bytes.iter().map(|v| v.len()).sum();
737        if is_upgrade {
738            self.gas_charger.charge_upgrade_package(total_bytes)?
739        } else {
740            self.gas_charger.charge_publish_package(total_bytes)?
741        }
742
743        let binary_config = self.env.protocol_config.binary_config(None);
744        let modules = module_bytes
745            .iter()
746            .map(|b| {
747                CompiledModule::deserialize_with_config(b, &binary_config)
748                    .map_err(|e| e.finish(Location::Undefined))
749            })
750            .collect::<VMResult<Vec<CompiledModule>>>()
751            .map_err(|e| self.env.convert_vm_error(e))?;
752        Ok(modules)
753    }
754
755    fn fetch_package(
756        &mut self,
757        dependency_id: &ObjectID,
758    ) -> Result<Rc<MovePackage>, ExecutionError> {
759        let [fetched_package] = self.fetch_packages(&[*dependency_id])?.try_into().map_err(
760            |_| {
761                make_invariant_violation!(
762                    "We should always fetch a single package for each object or return a dependency error."
763                )
764            },
765        )?;
766        Ok(fetched_package)
767    }
768
769    fn fetch_packages(
770        &mut self,
771        dependency_ids: &[ObjectID],
772    ) -> Result<Vec<Rc<MovePackage>>, ExecutionError> {
773        let mut fetched = vec![];
774        let mut missing = vec![];
775
776        // Collect into a set to avoid duplicate fetches and preserve existing behavior
777        let dependency_ids: BTreeSet<_> = dependency_ids.iter().collect();
778
779        for id in &dependency_ids {
780            match self.env.linkable_store.get_package(id) {
781                Err(e) => {
782                    return Err(ExecutionError::new_with_source(
783                        ExecutionErrorKind::PublishUpgradeMissingDependency,
784                        e,
785                    ));
786                }
787                Ok(Some(inner)) => {
788                    fetched.push(inner);
789                }
790                Ok(None) => {
791                    missing.push(*id);
792                }
793            }
794        }
795
796        if missing.is_empty() {
797            assert_invariant!(
798                fetched.len() == dependency_ids.len(),
799                "all dependencies should be fetched"
800            );
801            Ok(fetched)
802        } else {
803            let msg = format!(
804                "Missing dependencies: {}",
805                missing
806                    .into_iter()
807                    .map(|dep| format!("{}", dep))
808                    .collect::<Vec<_>>()
809                    .join(", ")
810            );
811            Err(ExecutionError::new_with_source(
812                ExecutionErrorKind::PublishUpgradeMissingDependency,
813                msg,
814            ))
815        }
816    }
817
818    fn publish_and_verify_modules(
819        &mut self,
820        package_id: ObjectID,
821        modules: &[CompiledModule],
822        linkage: &RootedLinkage,
823    ) -> Result<(), ExecutionError> {
824        // TODO(https://github.com/MystenLabs/sui/issues/69): avoid this redundant serialization by exposing VM API that allows us to run the linker directly on `Vec<CompiledModule>`
825        let binary_version = self.env.protocol_config.move_binary_format_version();
826        let new_module_bytes: Vec<_> = modules
827            .iter()
828            .map(|m| {
829                let mut bytes = Vec::new();
830                let version = if binary_version > VERSION_6 {
831                    m.version
832                } else {
833                    VERSION_6
834                };
835                m.serialize_with_version(version, &mut bytes).unwrap();
836                bytes
837            })
838            .collect();
839        let mut data_store = LinkedDataStore::new(linkage, self.env.linkable_store);
840        self.env
841            .vm
842            .get_runtime()
843            .publish_module_bundle(
844                new_module_bytes,
845                AccountAddress::from(package_id),
846                &mut data_store,
847                &mut SuiGasMeter(self.gas_charger.move_gas_status_mut()),
848            )
849            .map_err(|e| self.env.convert_linked_vm_error(e, linkage))?;
850
851        // run the Sui verifier
852        for module in modules {
853            // Run Sui bytecode verifier, which runs some additional checks that assume the Move
854            // bytecode verifier has passed.
855            sui_verifier::verifier::sui_verify_module_unmetered(
856                module,
857                &BTreeMap::new(),
858                &self
859                    .env
860                    .protocol_config
861                    .verifier_config(/* signing_limits */ None),
862            )?;
863        }
864
865        Ok(())
866    }
867
868    fn init_modules(
869        &mut self,
870        package_id: ObjectID,
871        modules: &[CompiledModule],
872        linkage: &RootedLinkage,
873        trace_builder_opt: &mut Option<MoveTraceBuilder>,
874    ) -> Result<(), ExecutionError> {
875        for module in modules {
876            let Some((fdef_idx, fdef)) = module.find_function_def_by_name(INIT_FN_NAME.as_str())
877            else {
878                continue;
879            };
880            let fhandle = module.function_handle_at(fdef.function);
881            let fparameters = module.signature_at(fhandle.parameters);
882            assert_invariant!(
883                fparameters.0.len() <= 2,
884                "init function should have at most 2 parameters"
885            );
886            let has_otw = fparameters.0.len() == 2;
887            let tx_context = self
888                .location(UsageKind::Borrow, T::Location::TxContext)
889                .map_err(|e| {
890                    make_invariant_violation!("Failed to get tx context for init function: {}", e)
891                })?;
892            // balance the stack after borrowing the tx context
893            charge_gas!(self, charge_store_loc, &tx_context)?;
894
895            let args = if has_otw {
896                vec![CtxValue(Value::one_time_witness()?), CtxValue(tx_context)]
897            } else {
898                vec![CtxValue(tx_context)]
899            };
900            debug_assert_eq!(self.gas_charger.move_gas_status().stack_height_current(), 0);
901            trace_utils::trace_move_call_start(trace_builder_opt);
902            let return_values = self.execute_function_bypass_visibility(
903                &module.self_id(),
904                INIT_FN_NAME,
905                &[],
906                args,
907                linkage,
908                trace_builder_opt,
909            )?;
910            trace_utils::trace_move_call_end(trace_builder_opt);
911
912            let storage_id = ModuleId::new(package_id.into(), module.self_id().name().to_owned());
913            self.take_user_events(
914                storage_id,
915                fdef_idx,
916                fdef.code.as_ref().map(|c| c.code.len() as u16).unwrap_or(0),
917                linkage,
918            )?;
919            assert_invariant!(
920                return_values.is_empty(),
921                "init should not have return values"
922            );
923            debug_assert_eq!(self.gas_charger.move_gas_status().stack_height_current(), 0);
924        }
925
926        Ok(())
927    }
928
929    pub fn publish_and_init_package<Mode: ExecutionMode>(
930        &mut self,
931        mut modules: Vec<CompiledModule>,
932        dep_ids: &[ObjectID],
933        linkage: ResolvedLinkage,
934        trace_builder_opt: &mut Option<MoveTraceBuilder>,
935    ) -> Result<ObjectID, ExecutionError> {
936        let runtime_id = if <Mode>::packages_are_predefined() {
937            // do not calculate or substitute id for predefined packages
938            (*modules[0].self_id().address()).into()
939        } else {
940            // It should be fine that this does not go through the object runtime since it does not
941            // need to know about new packages created, since Move objects and Move packages
942            // cannot interact
943            let id = self.tx_context.borrow_mut().fresh_id();
944            adapter::substitute_package_id(&mut modules, id)?;
945            id
946        };
947
948        let dependencies = self.fetch_packages(dep_ids)?;
949        let package = Rc::new(MovePackage::new_initial(
950            &modules,
951            self.env.protocol_config,
952            dependencies.iter().map(|p| p.as_ref()),
953        )?);
954        let package_id = package.id();
955
956        // Here we optimistically push the package that is being published/upgraded
957        // and if there is an error of any kind (verification or module init) we
958        // remove it.
959        // The call to `pop_last_package` later is fine because we cannot re-enter and
960        // the last package we pushed is the one we are verifying and running the init from
961        let linkage = RootedLinkage::new_for_publication(package_id, runtime_id, linkage);
962
963        self.env.linkable_store.push_package(package_id, package)?;
964        let res = self
965            .publish_and_verify_modules(runtime_id, &modules, &linkage)
966            .and_then(|_| self.init_modules(package_id, &modules, &linkage, trace_builder_opt));
967        match res {
968            Ok(()) => Ok(runtime_id),
969            Err(e) => {
970                self.env.linkable_store.pop_package(package_id)?;
971                Err(e)
972            }
973        }
974    }
975
976    pub fn upgrade(
977        &mut self,
978        mut modules: Vec<CompiledModule>,
979        dep_ids: &[ObjectID],
980        current_package_id: ObjectID,
981        upgrade_ticket_policy: u8,
982        linkage: ResolvedLinkage,
983    ) -> Result<ObjectID, ExecutionError> {
984        // Check that this package ID points to a package and get the package we're upgrading.
985        let current_move_package = self.fetch_package(&current_package_id)?;
986
987        let runtime_id = current_move_package.original_package_id();
988        adapter::substitute_package_id(&mut modules, runtime_id)?;
989
990        // Upgraded packages share their predecessor's runtime ID but get a new storage ID.
991        // It should be fine that this does not go through the object runtime since it does not
992        // need to know about new packages created, since Move objects and Move packages
993        // cannot interact
994        let storage_id = self.tx_context.borrow_mut().fresh_id();
995
996        let dependencies = self.fetch_packages(dep_ids)?;
997        let package = current_move_package.new_upgraded(
998            storage_id,
999            &modules,
1000            self.env.protocol_config,
1001            dependencies.iter().map(|p| p.as_ref()),
1002        )?;
1003
1004        let linkage = RootedLinkage::new_for_publication(storage_id, runtime_id, linkage);
1005        self.publish_and_verify_modules(runtime_id, &modules, &linkage)?;
1006
1007        legacy_ptb::execution::check_compatibility(
1008            self.env.protocol_config,
1009            current_move_package.as_ref(),
1010            &modules,
1011            upgrade_ticket_policy,
1012        )?;
1013
1014        if self.env.protocol_config.check_for_init_during_upgrade() {
1015            // find newly added modules to the package,
1016            // and error if they have init functions
1017            let current_module_names: BTreeSet<&str> = current_move_package
1018                .serialized_module_map()
1019                .keys()
1020                .map(|s| s.as_str())
1021                .collect();
1022            let upgrade_module_names: BTreeSet<&str> = package
1023                .serialized_module_map()
1024                .keys()
1025                .map(|s| s.as_str())
1026                .collect();
1027            let new_module_names = upgrade_module_names
1028                .difference(&current_module_names)
1029                .copied()
1030                .collect::<BTreeSet<&str>>();
1031            let new_modules = modules
1032                .iter()
1033                .filter(|m| {
1034                    let name = m.identifier_at(m.self_handle().name).as_str();
1035                    new_module_names.contains(name)
1036                })
1037                .collect::<Vec<&CompiledModule>>();
1038            let new_module_has_init = new_modules.iter().any(|module| {
1039                module.function_defs.iter().any(|fdef| {
1040                    let fhandle = module.function_handle_at(fdef.function);
1041                    let fname = module.identifier_at(fhandle.name);
1042                    fname == INIT_FN_NAME
1043                })
1044            });
1045            if new_module_has_init {
1046                // TODO we cannot run 'init' on upgrade yet due to global type cache limitations
1047                return Err(ExecutionError::new_with_source(
1048                    ExecutionErrorKind::FeatureNotYetSupported,
1049                    "`init` in new modules on upgrade is not yet supported",
1050                ));
1051            }
1052        }
1053
1054        self.env
1055            .linkable_store
1056            .push_package(storage_id, Rc::new(package))?;
1057        Ok(storage_id)
1058    }
1059
1060    //
1061    // Commands
1062    //
1063
1064    pub fn transfer_object(
1065        &mut self,
1066        recipient: Owner,
1067        ty: Type,
1068        object: CtxValue,
1069    ) -> Result<(), ExecutionError> {
1070        self.transfer_object_(recipient, ty, object, /* end of transaction */ false)
1071    }
1072
1073    fn transfer_object_(
1074        &mut self,
1075        recipient: Owner,
1076        ty: Type,
1077        object: CtxValue,
1078        end_of_transaction: bool,
1079    ) -> Result<(), ExecutionError> {
1080        let tag = TypeTag::try_from(ty)
1081            .map_err(|_| make_invariant_violation!("Unable to convert Type to TypeTag"))?;
1082        let TypeTag::Struct(tag) = tag else {
1083            invariant_violation!("Expected struct type tag");
1084        };
1085        let ty = MoveObjectType::from(*tag);
1086        object_runtime_mut!(self)?
1087            .transfer(recipient, ty, object.0.into(), end_of_transaction)
1088            .map_err(|e| self.env.convert_vm_error(e.finish(Location::Undefined)))?;
1089        Ok(())
1090    }
1091
1092    //
1093    // Dev Inspect tracking
1094    //
1095
1096    pub fn argument_updates(
1097        &mut self,
1098        args: Vec<T::Argument>,
1099    ) -> Result<Vec<(sui_types::transaction::Argument, Vec<u8>, TypeTag)>, ExecutionError> {
1100        args.into_iter()
1101            .filter_map(|arg| self.argument_update(arg).transpose())
1102            .collect()
1103    }
1104
1105    fn argument_update(
1106        &mut self,
1107        sp!(_, (arg, ty)): T::Argument,
1108    ) -> Result<Option<(sui_types::transaction::Argument, Vec<u8>, TypeTag)>, ExecutionError> {
1109        use sui_types::transaction::Argument as TxArgument;
1110        let ty = match ty {
1111            Type::Reference(true, inner) => (*inner).clone(),
1112            ty => {
1113                debug_assert!(
1114                    false,
1115                    "Unexpected non reference type in location update: {ty:?}"
1116                );
1117                return Ok(None);
1118            }
1119        };
1120        let Ok(tag): Result<TypeTag, _> = ty.clone().try_into() else {
1121            invariant_violation!("unable to generate type tag from type")
1122        };
1123        let location = arg.location();
1124        let resolved = self.locations.resolve(location)?;
1125        let local = match resolved {
1126            ResolvedLocation::Local(local)
1127            | ResolvedLocation::Pure { local, .. }
1128            | ResolvedLocation::Receiving { local, .. } => local,
1129        };
1130        if local.is_invalid()? {
1131            return Ok(None);
1132        }
1133        // copy the value from the local
1134        let value = local.copy()?;
1135        let value = match arg {
1136            T::Argument__::Use(_) => {
1137                // dereference the reference
1138                value.read_ref()?
1139            }
1140            T::Argument__::Borrow(_, _) => {
1141                // value is not a reference, nothing to do
1142                value
1143            }
1144            T::Argument__::Freeze(_) => {
1145                invariant_violation!("freeze should not be used for a mutable reference")
1146            }
1147            T::Argument__::Read(_) => {
1148                invariant_violation!("read should not return a reference")
1149            }
1150        };
1151        let layout = self.env.runtime_layout(&ty)?;
1152        let Some(bytes) = value.typed_serialize(&layout) else {
1153            invariant_violation!("Failed to serialize Move value");
1154        };
1155        let arg = match location {
1156            T::Location::TxContext => return Ok(None),
1157            T::Location::GasCoin => TxArgument::GasCoin,
1158            T::Location::Result(i, j) => TxArgument::NestedResult(i, j),
1159            T::Location::ObjectInput(i) => {
1160                TxArgument::Input(self.locations.input_object_metadata[i as usize].0.0)
1161            }
1162            T::Location::WithdrawalInput(i) => TxArgument::Input(
1163                self.locations.input_withdrawal_metadata[i as usize]
1164                    .original_input_index
1165                    .0,
1166            ),
1167            T::Location::PureInput(i) => TxArgument::Input(
1168                self.locations.pure_input_metadata[i as usize]
1169                    .original_input_index
1170                    .0,
1171            ),
1172            T::Location::ReceivingInput(i) => TxArgument::Input(
1173                self.locations.receiving_input_metadata[i as usize]
1174                    .original_input_index
1175                    .0,
1176            ),
1177        };
1178        Ok(Some((arg, bytes, tag)))
1179    }
1180
1181    pub fn tracked_results(
1182        &self,
1183        results: &[CtxValue],
1184        result_tys: &T::ResultType,
1185    ) -> Result<Vec<(Vec<u8>, TypeTag)>, ExecutionError> {
1186        assert_invariant!(
1187            results.len() == result_tys.len(),
1188            "results and result types should match"
1189        );
1190        results
1191            .iter()
1192            .zip(result_tys)
1193            .map(|(v, ty)| self.tracked_result(&v.0, ty.clone()))
1194            .collect()
1195    }
1196
1197    fn tracked_result(
1198        &self,
1199        result: &Value,
1200        ty: Type,
1201    ) -> Result<(Vec<u8>, TypeTag), ExecutionError> {
1202        let inner_value;
1203        let (v, ty) = match ty {
1204            Type::Reference(_, inner) => {
1205                inner_value = result.copy()?.read_ref()?;
1206                (&inner_value, (*inner).clone())
1207            }
1208            _ => (result, ty),
1209        };
1210        let layout = self.env.runtime_layout(&ty)?;
1211        let Some(bytes) = v.typed_serialize(&layout) else {
1212            invariant_violation!("Failed to serialize Move value");
1213        };
1214        let Ok(tag): Result<TypeTag, _> = ty.try_into() else {
1215            invariant_violation!("unable to generate type tag from type")
1216        };
1217        Ok((bytes, tag))
1218    }
1219}
1220
1221impl VMValueCast<CtxValue> for VMValue {
1222    fn cast(self) -> Result<CtxValue, PartialVMError> {
1223        Ok(CtxValue(self.into()))
1224    }
1225}
1226
1227impl CtxValue {
1228    pub fn vec_pack(ty: Type, values: Vec<CtxValue>) -> Result<CtxValue, ExecutionError> {
1229        Ok(CtxValue(Value::vec_pack(
1230            ty,
1231            values.into_iter().map(|v| v.0).collect(),
1232        )?))
1233    }
1234
1235    pub fn coin_ref_value(self) -> Result<u64, ExecutionError> {
1236        self.0.coin_ref_value()
1237    }
1238
1239    pub fn coin_ref_subtract_balance(self, amount: u64) -> Result<(), ExecutionError> {
1240        self.0.coin_ref_subtract_balance(amount)
1241    }
1242
1243    pub fn coin_ref_add_balance(self, amount: u64) -> Result<(), ExecutionError> {
1244        self.0.coin_ref_add_balance(amount)
1245    }
1246
1247    pub fn into_upgrade_ticket(self) -> Result<UpgradeTicket, ExecutionError> {
1248        self.0.into_upgrade_ticket()
1249    }
1250
1251    /// Used to get access the inner Value for tracing.
1252    pub(super) fn inner_for_tracing(&self) -> &Value {
1253        &self.0
1254    }
1255}
1256
1257fn load_object_arg(
1258    meter: &mut GasCharger,
1259    env: &Env,
1260    input_object_map: &mut BTreeMap<ObjectID, object_runtime::InputObject>,
1261    input: T::ObjectInput,
1262) -> Result<(T::InputIndex, InputObjectMetadata, Value), ExecutionError> {
1263    let id = input.arg.id();
1264    let mutability = input.arg.mutability();
1265    let (metadata, value) =
1266        load_object_arg_impl(meter, env, input_object_map, id, mutability, input.ty)?;
1267    Ok((input.original_input_index, metadata, value))
1268}
1269
1270fn load_object_arg_impl(
1271    meter: &mut GasCharger,
1272    env: &Env,
1273    input_object_map: &mut BTreeMap<ObjectID, object_runtime::InputObject>,
1274    id: ObjectID,
1275    mutability: ObjectMutability,
1276    ty: T::Type,
1277) -> Result<(InputObjectMetadata, Value), ExecutionError> {
1278    let obj = env.read_object(&id)?;
1279    let owner = obj.owner.clone();
1280    let version = obj.version();
1281    let object_metadata = InputObjectMetadata {
1282        id,
1283        mutability,
1284        owner: owner.clone(),
1285        version,
1286        type_: ty.clone(),
1287    };
1288    let sui_types::object::ObjectInner {
1289        data: sui_types::object::Data::Move(move_obj),
1290        ..
1291    } = obj.as_inner()
1292    else {
1293        invariant_violation!("Expected a Move object");
1294    };
1295    assert_expected_move_object_type(&object_metadata.type_, move_obj.type_())?;
1296    let contained_uids = {
1297        let fully_annotated_layout = env.fully_annotated_layout(&ty)?;
1298        get_all_uids(&fully_annotated_layout, move_obj.contents()).map_err(|e| {
1299            make_invariant_violation!("Unable to retrieve UIDs for object. Got error: {e}")
1300        })?
1301    };
1302    input_object_map.insert(
1303        id,
1304        object_runtime::InputObject {
1305            contained_uids,
1306            version,
1307            owner,
1308        },
1309    );
1310
1311    let v = Value::deserialize(env, move_obj.contents(), ty)?;
1312    charge_gas_!(meter, env, charge_copy_loc, &v)?;
1313    charge_gas_!(meter, env, charge_store_loc, &v)?;
1314    Ok((object_metadata, v))
1315}
1316
1317fn load_withdrawal_arg(
1318    meter: &mut GasCharger,
1319    env: &Env,
1320    withdrawal: &T::WithdrawalInput,
1321) -> Result<Value, ExecutionError> {
1322    let T::WithdrawalInput {
1323        original_input_index: _,
1324        ty: _,
1325        owner,
1326        amount,
1327    } = withdrawal;
1328    let loaded = Value::funds_accumulator_withdrawal(*owner, *amount);
1329    charge_gas_!(meter, env, charge_copy_loc, &loaded)?;
1330    charge_gas_!(meter, env, charge_store_loc, &loaded)?;
1331    Ok(loaded)
1332}
1333
1334fn load_pure_value(
1335    meter: &mut GasCharger,
1336    env: &Env,
1337    bytes: &[u8],
1338    metadata: &T::PureInput,
1339) -> Result<Value, ExecutionError> {
1340    let loaded = Value::deserialize(env, bytes, metadata.ty.clone())?;
1341    // ByteValue::Receiving { id, version } => Value::receiving(*id, *version),
1342    charge_gas_!(meter, env, charge_copy_loc, &loaded)?;
1343    charge_gas_!(meter, env, charge_store_loc, &loaded)?;
1344    Ok(loaded)
1345}
1346
1347fn load_receiving_value(
1348    meter: &mut GasCharger,
1349    env: &Env,
1350    metadata: &T::ReceivingInput,
1351) -> Result<Value, ExecutionError> {
1352    let (id, version, _) = metadata.object_ref;
1353    let loaded = Value::receiving(id, version);
1354    charge_gas_!(meter, env, charge_copy_loc, &loaded)?;
1355    charge_gas_!(meter, env, charge_store_loc, &loaded)?;
1356    Ok(loaded)
1357}
1358
1359fn copy_value(meter: &mut GasCharger, env: &Env, value: &Value) -> Result<Value, ExecutionError> {
1360    charge_gas_!(meter, env, charge_copy_loc, value)?;
1361    charge_gas_!(meter, env, charge_pop, value)?;
1362    value.copy()
1363}
1364
1365/// The max budget was deducted from the gas coin at the beginning of the transaction,
1366/// now we return exactly that amount. Gas will be charged by the execution engine
1367fn refund_max_gas_budget<OType>(
1368    writes: &mut IndexMap<ObjectID, (Owner, OType, VMValue)>,
1369    gas_charger: &mut GasCharger,
1370    gas_id: ObjectID,
1371) -> Result<(), ExecutionError> {
1372    let Some((_, _, value_ref)) = writes.get_mut(&gas_id) else {
1373        invariant_violation!("Gas object cannot be wrapped or destroyed")
1374    };
1375    // replace with dummy value
1376    let value = std::mem::replace(value_ref, VMValue::u8(0));
1377    let mut locals = Locals::new([Some(value.into())])?;
1378    let mut local = locals.local(0)?;
1379    let coin_value = local.borrow()?.coin_ref_value()?;
1380    let additional = gas_charger.gas_budget();
1381    if coin_value.checked_add(additional).is_none() {
1382        return Err(ExecutionError::new_with_source(
1383            ExecutionErrorKind::CoinBalanceOverflow,
1384            "Gas coin too large after returning the max gas budget",
1385        ));
1386    };
1387    local.borrow()?.coin_ref_add_balance(additional)?;
1388    // put the value back
1389    *value_ref = local.move_()?.into();
1390    Ok(())
1391}
1392
1393/// Generate an MoveObject given an updated/written object
1394/// # Safety
1395///
1396/// This function assumes proper generation of has_public_transfer, either from the abilities of
1397/// the StructTag, or from the runtime correctly propagating from the inputs
1398unsafe fn create_written_object<Mode: ExecutionMode>(
1399    env: &Env,
1400    objects_modified_at: &BTreeMap<ObjectID, LoadedRuntimeObject>,
1401    id: ObjectID,
1402    type_: Type,
1403    has_public_transfer: bool,
1404    contents: Vec<u8>,
1405) -> Result<MoveObject, ExecutionError> {
1406    debug_assert_eq!(
1407        id,
1408        MoveObject::id_opt(&contents).expect("object contents should start with an id")
1409    );
1410    let old_obj_ver = objects_modified_at
1411        .get(&id)
1412        .map(|obj: &LoadedRuntimeObject| obj.version);
1413
1414    let Ok(type_tag): Result<TypeTag, _> = type_.try_into() else {
1415        invariant_violation!("unable to generate type tag from type")
1416    };
1417
1418    let struct_tag = match type_tag {
1419        TypeTag::Struct(inner) => *inner,
1420        _ => invariant_violation!("Non struct type for object"),
1421    };
1422    unsafe {
1423        MoveObject::new_from_execution(
1424            struct_tag.into(),
1425            has_public_transfer,
1426            old_obj_ver.unwrap_or_default(),
1427            contents,
1428            env.protocol_config,
1429            Mode::packages_are_predefined(),
1430        )
1431    }
1432}
1433
1434/// Assert the type inferred matches the object's type. This has already been done during loading,
1435/// but is checked again as an invariant. This may be removed safely at a later time if needed.
1436fn assert_expected_move_object_type(
1437    actual: &Type,
1438    expected: &MoveObjectType,
1439) -> Result<(), ExecutionError> {
1440    let Type::Datatype(actual) = actual else {
1441        invariant_violation!("Expected a datatype for a Move object");
1442    };
1443    let (a, m, n) = actual.qualified_ident();
1444    assert_invariant!(
1445        a == &expected.address(),
1446        "Actual address does not match expected. actual: {actual:?} vs expected: {expected:?}"
1447    );
1448    assert_invariant!(
1449        m == expected.module(),
1450        "Actual module does not match expected. actual: {actual:?} vs expected: {expected:?}"
1451    );
1452    assert_invariant!(
1453        n == expected.name(),
1454        "Actual struct does not match expected. actual: {actual:?} vs expected: {expected:?}"
1455    );
1456    let actual_type_arguments = &actual.type_arguments;
1457    let expected_type_arguments = expected.type_params();
1458    assert_invariant!(
1459        actual_type_arguments.len() == expected_type_arguments.len(),
1460        "Actual type arg length does not match expected. \
1461       actual: {actual:?} vs expected: {expected:?}",
1462    );
1463    for (actual_ty, expected_ty) in actual_type_arguments.iter().zip(&expected_type_arguments) {
1464        assert_expected_type(actual_ty, expected_ty)?;
1465    }
1466    Ok(())
1467}
1468
1469/// Assert the type inferred matches the expected type. This has already been done during typing,
1470/// but is checked again as an invariant. This may be removed safely at a later time if needed.
1471fn assert_expected_type(actual: &Type, expected: &TypeTag) -> Result<(), ExecutionError> {
1472    match (actual, expected) {
1473        (Type::Bool, TypeTag::Bool)
1474        | (Type::U8, TypeTag::U8)
1475        | (Type::U16, TypeTag::U16)
1476        | (Type::U32, TypeTag::U32)
1477        | (Type::U64, TypeTag::U64)
1478        | (Type::U128, TypeTag::U128)
1479        | (Type::U256, TypeTag::U256)
1480        | (Type::Address, TypeTag::Address)
1481        | (Type::Signer, TypeTag::Signer) => Ok(()),
1482        (Type::Vector(inner_actual), TypeTag::Vector(inner_expected)) => {
1483            assert_expected_type(&inner_actual.element_type, inner_expected)
1484        }
1485        (Type::Datatype(actual_dt), TypeTag::Struct(expected_st)) => {
1486            assert_expected_data_type(actual_dt, expected_st)
1487        }
1488        _ => invariant_violation!(
1489            "Type mismatch between actual: {actual:?} and expected: {expected:?}"
1490        ),
1491    }
1492}
1493/// Assert the type inferred matches the expected type. This has already been done during typing,
1494/// but is checked again as an invariant. This may be removed safely at a later time if needed.
1495fn assert_expected_data_type(
1496    actual: &Datatype,
1497    expected: &StructTag,
1498) -> Result<(), ExecutionError> {
1499    let (a, m, n) = actual.qualified_ident();
1500    assert_invariant!(
1501        a == &expected.address,
1502        "Actual address does not match expected. actual: {actual:?} vs expected: {expected:?}"
1503    );
1504    assert_invariant!(
1505        m == expected.module.as_ident_str(),
1506        "Actual module does not match expected. actual: {actual:?} vs expected: {expected:?}"
1507    );
1508    assert_invariant!(
1509        n == expected.name.as_ident_str(),
1510        "Actual struct does not match expected. actual: {actual:?} vs expected: {expected:?}"
1511    );
1512    let actual_type_arguments = &actual.type_arguments;
1513    let expected_type_arguments = &expected.type_params;
1514    assert_invariant!(
1515        actual_type_arguments.len() == expected_type_arguments.len(),
1516        "Actual type arg length does not match expected. \
1517       actual: {actual:?} vs expected: {expected:?}",
1518    );
1519    for (actual_ty, expected_ty) in actual_type_arguments.iter().zip(expected_type_arguments) {
1520        assert_expected_type(actual_ty, expected_ty)?;
1521    }
1522    Ok(())
1523}